diff options
115 files changed, 6921 insertions, 1463 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 60a183c117..1b40446c73 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -842,6 +842,7 @@ M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org S: Maintained F: hw/*/versatile* +F: include/hw/i2c/arm_sbcon_i2c.h F: hw/misc/arm_sysctl.c F: docs/system/arm/versatile.rst @@ -1250,7 +1251,20 @@ M: Alistair Francis <Alistair.Francis@wdc.com> L: qemu-riscv@nongnu.org S: Supported F: hw/riscv/opentitan.c +F: hw/char/ibex_uart.c +F: hw/intc/ibex_plic.c F: include/hw/riscv/opentitan.h +F: include/hw/char/ibex_uart.h +F: include/hw/intc/ibex_plic.h + +RX Machines +----------- +rx-gdbsim +M: Yoshinori Sato <ysato@users.sourceforge.jp> +S: Maintained +F: docs/system/target-rx.rst +F: hw/rx/rx-gdbsim.c +F: tests/acceptance/machine_rx_gdbsim.py SH4 Machines ------------ @@ -1260,13 +1274,15 @@ R: Magnus Damm <magnus.damm@gmail.com> S: Maintained F: hw/sh4/r2d.c F: hw/intc/sh_intc.c -F: hw/timer/sh_timer.c +F: include/hw/sh4/sh_intc.h Shix M: Yoshinori Sato <ysato@users.sourceforge.jp> R: Magnus Damm <magnus.damm@gmail.com> S: Odd Fixes F: hw/sh4/shix.c +F: hw/intc/sh_intc.c +F: include/hw/sh4/sh_intc.h SPARC Machines -------------- @@ -1961,6 +1977,26 @@ F: hw/*/*xive* F: include/hw/*/*xive* F: docs/*/*xive* +Renesas peripherals +M: Yoshinori Sato <ysato@users.sourceforge.jp> +R: Magnus Damm <magnus.damm@gmail.com> +S: Maintained +F: hw/char/renesas_sci.c +F: hw/char/sh_serial.c +F: hw/timer/renesas_*.c +F: hw/timer/sh_timer.c +F: include/hw/char/renesas_sci.h +F: include/hw/sh4/sh.h +F: include/hw/timer/renesas_*.h + +Renesas RX peripherals +M: Yoshinori Sato <ysato@users.sourceforge.jp> +S: Maintained +F: hw/intc/rx_icu.c +F: hw/rx/ +F: include/hw/intc/rx_icu.h +F: include/hw/rx/ + Subsystems ---------- Audio @@ -2534,6 +2570,7 @@ F: net/filter-replay.c F: include/sysemu/replay.h F: docs/replay.txt F: stubs/replay.c +F: tests/acceptance/replay_kernel.py IOVA Tree M: Peter Xu <peterx@redhat.com> @@ -2941,6 +2978,14 @@ S: Maintained F: tests/tcg/Makefile F: tests/tcg/Makefile.include +Acceptance (Integration) Testing with the Avocado framework +W: https://trello.com/b/6Qi1pxVn/avocado-qemu +R: Cleber Rosa <crosa@redhat.com> +R: Philippe Mathieu-Daudé <philmd@redhat.com> +R: Wainer dos Santos Moschetta <wainersm@redhat.com> +S: Odd Fixes +F: tests/acceptance/ + Documentation ------------- Build system architecture diff --git a/audio/jackaudio.c b/audio/jackaudio.c index 722ddb1dfe..72ed7c4929 100644 --- a/audio/jackaudio.c +++ b/audio/jackaudio.c @@ -38,7 +38,6 @@ struct QJack; typedef enum QJackState { QJACK_STATE_DISCONNECTED, - QJACK_STATE_STOPPED, QJACK_STATE_RUNNING, QJACK_STATE_SHUTDOWN } @@ -57,7 +56,7 @@ typedef struct QJackClient { AudiodevJackPerDirectionOptions *opt; bool out; - bool finished; + bool enabled; bool connect_ports; int packets; @@ -272,9 +271,17 @@ static int qjack_process(jack_nframes_t nframes, void *arg) } if (c->out) { - qjack_buffer_read_l(&c->fifo, buffers, nframes); + if (likely(c->enabled)) { + qjack_buffer_read_l(&c->fifo, buffers, nframes); + } else { + for(int i = 0; i < c->nchannels; ++i) { + memset(buffers[i], 0, nframes * sizeof(float)); + } + } } else { - qjack_buffer_write_l(&c->fifo, buffers, nframes); + if (likely(c->enabled)) { + qjack_buffer_write_l(&c->fifo, buffers, nframes); + } } return 0; @@ -315,8 +322,8 @@ static void qjack_client_recover(QJackClient *c) if (c->state == QJACK_STATE_DISCONNECTED && c->packets % 100 == 0) { - /* if not finished then attempt to recover */ - if (!c->finished) { + /* if enabled then attempt to recover */ + if (c->enabled) { dolog("attempting to reconnect to server\n"); qjack_client_init(c); } @@ -388,7 +395,10 @@ static int qjack_client_init(QJackClient *c) char client_name[jack_client_name_size()]; jack_options_t options = JackNullOption; - c->finished = false; + if (c->state == QJACK_STATE_RUNNING) { + return 0; + } + c->connect_ports = true; snprintf(client_name, sizeof(client_name), "%s-%s", @@ -434,17 +444,6 @@ static int qjack_client_init(QJackClient *c) jack_set_xrun_callback(c->client, qjack_xrun, c); jack_on_shutdown(c->client, qjack_shutdown, c); - /* - * ensure the buffersize is no smaller then 512 samples, some (all?) qemu - * virtual devices do not work correctly otherwise - */ - if (c->buffersize < 512) { - c->buffersize = 512; - } - - /* create a 2 period buffer */ - qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2); - /* allocate and register the ports */ c->port = g_malloc(sizeof(jack_port_t *) * c->nchannels); for (int i = 0; i < c->nchannels; ++i) { @@ -468,6 +467,17 @@ static int qjack_client_init(QJackClient *c) jack_activate(c->client); c->buffersize = jack_get_buffer_size(c->client); + /* + * ensure the buffersize is no smaller then 512 samples, some (all?) qemu + * virtual devices do not work correctly otherwise + */ + if (c->buffersize < 512) { + c->buffersize = 512; + } + + /* create a 2 period buffer */ + qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2); + qjack_client_connect_ports(c); c->state = QJACK_STATE_RUNNING; return 0; @@ -479,13 +489,13 @@ static int qjack_init_out(HWVoiceOut *hw, struct audsettings *as, QJackOut *jo = (QJackOut *)hw; Audiodev *dev = (Audiodev *)drv_opaque; - if (jo->c.state != QJACK_STATE_DISCONNECTED) { - return 0; - } + qjack_client_fini(&jo->c); jo->c.out = true; + jo->c.enabled = false; jo->c.nchannels = as->nchannels; jo->c.opt = dev->u.jack.out; + int ret = qjack_client_init(&jo->c); if (ret != 0) { return ret; @@ -515,13 +525,13 @@ static int qjack_init_in(HWVoiceIn *hw, struct audsettings *as, QJackIn *ji = (QJackIn *)hw; Audiodev *dev = (Audiodev *)drv_opaque; - if (ji->c.state != QJACK_STATE_DISCONNECTED) { - return 0; - } + qjack_client_fini(&ji->c); ji->c.out = false; + ji->c.enabled = false; ji->c.nchannels = as->nchannels; ji->c.opt = dev->u.jack.in; + int ret = qjack_client_init(&ji->c); if (ret != 0) { return ret; @@ -549,12 +559,6 @@ static void qjack_client_fini(QJackClient *c) { switch (c->state) { case QJACK_STATE_RUNNING: - /* fallthrough */ - - case QJACK_STATE_STOPPED: - for (int i = 0; i < c->nchannels; ++i) { - jack_port_unregister(c->client, c->port[i]); - } jack_deactivate(c->client); /* fallthrough */ @@ -575,23 +579,25 @@ static void qjack_client_fini(QJackClient *c) static void qjack_fini_out(HWVoiceOut *hw) { QJackOut *jo = (QJackOut *)hw; - jo->c.finished = true; qjack_client_fini(&jo->c); } static void qjack_fini_in(HWVoiceIn *hw) { QJackIn *ji = (QJackIn *)hw; - ji->c.finished = true; qjack_client_fini(&ji->c); } static void qjack_enable_out(HWVoiceOut *hw, bool enable) { + QJackOut *jo = (QJackOut *)hw; + jo->c.enabled = enable; } static void qjack_enable_in(HWVoiceIn *hw, bool enable) { + QJackIn *ji = (QJackIn *)hw; + ji->c.enabled = enable; } static int qjack_thread_creator(jack_native_thread_t *thread, @@ -611,9 +617,6 @@ static int qjack_thread_creator(jack_native_thread_t *thread, static void *qjack_init(Audiodev *dev) { assert(dev->driver == AUDIODEV_DRIVER_JACK); - - dev->u.jack.has_in = false; - return dev; } diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak index 7c4eb2c1a0..df2b4e4f42 100644 --- a/default-configs/rx-softmmu.mak +++ b/default-configs/rx-softmmu.mak @@ -1,2 +1,3 @@ # Default configuration for rx-softmmu +CONFIG_RX_GDBSIM=y diff --git a/docs/system/s390x/vfio-ap.rst b/docs/system/s390x/vfio-ap.rst index 3cd84179a2..084ba9c4e3 100644 --- a/docs/system/s390x/vfio-ap.rst +++ b/docs/system/s390x/vfio-ap.rst @@ -606,10 +606,11 @@ action. To hot plug a vfio-ap device, use the QEMU ``device_add`` command:: - (qemu) device_add vfio-ap,sysfsdev="$path-to-mdev" + (qemu) device_add vfio-ap,sysfsdev="$path-to-mdev",id="$id" Where the ``$path-to-mdev`` value specifies the absolute path to a mediated device to which AP resources to be used by the guest have been assigned. +``$id`` is the name value for the optional id parameter. Note that on Linux guests, the AP devices will be created in the ``/sys/bus/ap/devices`` directory when the AP bus subsequently performs its periodic @@ -632,10 +633,9 @@ or a prior hot plug action. To hot unplug a vfio-ap device, use the QEMU ``device_del`` command:: - (qemu) device_del vfio-ap,sysfsdev="$path-to-mdev" + (qemu) device_del "$id" -Where ``$path-to-mdev`` is the same as the path specified when the vfio-ap -device was attached to the virtual machine's ap-bus. +Where ``$id`` is the same id that was specified at device creation. On a Linux guest, the AP devices will be removed from the ``/sys/bus/ap/devices`` directory on the guest when the AP bus subsequently performs its periodic scan, diff --git a/docs/system/target-rx.rst b/docs/system/target-rx.rst new file mode 100644 index 0000000000..4a20a89a06 --- /dev/null +++ b/docs/system/target-rx.rst @@ -0,0 +1,36 @@ +.. _RX-System-emulator: + +RX System emulator +-------------------- + +Use the executable ``qemu-system-rx`` to simulate RX target (GDB simulator). +This target emulated following devices. + +- R5F562N8 MCU + + - On-chip memory (ROM 512KB, RAM 96KB) + - Interrupt Control Unit (ICUa) + - 8Bit Timer x 1CH (TMR0,1) + - Compare Match Timer x 2CH (CMT0,1) + - Serial Communication Interface x 1CH (SCI0) + +- External memory 16MByte + +Example of ``qemu-system-rx`` usage for RX is shown below: + +Download ``<u-boot_image_file>`` from +https://osdn.net/users/ysato/pf/qemu/dl/u-boot.bin.gz + +Start emulation of rx-virt:: + qemu-system-rx -M gdbsim-r5f562n8 -bios <u-boot_image_file> + +Download ``kernel_image_file`` from +https://osdn.net/users/ysato/pf/qemu/dl/zImage + +Download ``device_tree_blob`` from +https://osdn.net/users/ysato/pf/qemu/dl/rx-virt.dtb + +Start emulation of rx-virt:: + qemu-system-rx -M gdbsim-r5f562n8 \ + -kernel <kernel_image_file> -dtb <device_tree_blob> \ + -append "earlycon" diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 0d8f91580a..99435a3eba 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst @@ -18,3 +18,4 @@ Contents: target-m68k target-xtensa target-s390x + target-rx diff --git a/hw/Kconfig b/hw/Kconfig index ecf491bf04..62f9ebdc22 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -55,6 +55,7 @@ source nios2/Kconfig source openrisc/Kconfig source ppc/Kconfig source riscv/Kconfig +source rx/Kconfig source s390x/Kconfig source sh4/Kconfig source sparc/Kconfig diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 1cb34111e5..b8abdefa1c 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -193,6 +193,33 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, } } +static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + AcpiGedState *s = ACPI_GED(hotplug_dev); + + if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && + !(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) { + acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp); + } else { + error_setg(errp, "acpi: device unplug request for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } +} + +static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + AcpiGedState *s = ACPI_GED(hotplug_dev); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + acpi_memory_unplug_cb(&s->memhp_state, dev, errp); + } else { + error_setg(errp, "acpi: device unplug for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } +} + static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) { AcpiGedState *s = ACPI_GED(adev); @@ -318,6 +345,8 @@ static void acpi_ged_class_init(ObjectClass *class, void *data) dc->vmsd = &vmstate_acpi_ged; hc->plug = acpi_ged_device_plug_cb; + hc->unplug_request = acpi_ged_unplug_request_cb; + hc->unplug = acpi_ged_unplug_cb; adevc->send_event = acpi_ged_send_event; } diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 9afa6eee79..4a224a6351 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -59,7 +59,7 @@ config HIGHBANK select ARM_TIMER # sp804 select ARM_V7M select PL011 # UART - select PL022 # Serial port + select PL022 # SPI select PL031 # RTC select PL061 # GPIO select PL310 # cache controller @@ -222,7 +222,7 @@ config STELLARIS select CMSDK_APB_WATCHDOG select I2C select PL011 # UART - select PL022 # Serial port + select PL022 # SPI select PL061 # GPIO select SSD0303 # OLED display select SSD0323 # OLED display @@ -401,10 +401,12 @@ config MPS2 select MPS2_FPGAIO select MPS2_SCC select OR_IRQ - select PL022 # Serial port + select PL022 # SPI select PL080 # DMA controller select SPLIT_IRQ select UNIMP + select CMSDK_APB_WATCHDOG + select VERSATILE_I2C config FSL_IMX7 bool diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 8155c35418..a4fd5ddede 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -58,6 +58,7 @@ #include "hw/arm/armsse.h" #include "hw/dma/pl080.h" #include "hw/ssi/pl022.h" +#include "hw/i2c/arm_sbcon_i2c.h" #include "hw/net/lan9118.h" #include "net/net.h" #include "hw/core/split-irq.h" @@ -87,7 +88,7 @@ typedef struct { TZPPC ppc[5]; TZMPC ssram_mpc[3]; PL022State spi[5]; - UnimplementedDeviceState i2c[4]; + ArmSbconI2CState i2c[4]; UnimplementedDeviceState i2s_audio; UnimplementedDeviceState gpio[4]; UnimplementedDeviceState gfx; @@ -365,6 +366,18 @@ static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(s, 0); } +static MemoryRegion *make_i2c(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + ArmSbconI2CState *i2c = opaque; + SysBusDevice *s; + + object_initialize_child(OBJECT(mms), name, i2c, TYPE_ARM_SBCON_I2C); + s = SYS_BUS_DEVICE(i2c); + sysbus_realize(s, &error_fatal); + return sysbus_mmio_get_region(s, 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -499,10 +512,10 @@ static void mps2tz_common_init(MachineState *machine) { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 }, { "uart3", make_uart, &mms->uart[3], 0x40203000, 0x1000 }, { "uart4", make_uart, &mms->uart[4], 0x40204000, 0x1000 }, - { "i2c0", make_unimp_dev, &mms->i2c[0], 0x40207000, 0x1000 }, - { "i2c1", make_unimp_dev, &mms->i2c[1], 0x40208000, 0x1000 }, - { "i2c2", make_unimp_dev, &mms->i2c[2], 0x4020c000, 0x1000 }, - { "i2c3", make_unimp_dev, &mms->i2c[3], 0x4020d000, 0x1000 }, + { "i2c0", make_i2c, &mms->i2c[0], 0x40207000, 0x1000 }, + { "i2c1", make_i2c, &mms->i2c[1], 0x40208000, 0x1000 }, + { "i2c2", make_i2c, &mms->i2c[2], 0x4020c000, 0x1000 }, + { "i2c3", make_i2c, &mms->i2c[3], 0x4020d000, 0x1000 }, }, }, { .name = "apb_ppcexp2", diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index daa55f730b..d1653a7e6e 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -38,8 +38,12 @@ #include "hw/timer/cmsdk-apb-timer.h" #include "hw/timer/cmsdk-apb-dualtimer.h" #include "hw/misc/mps2-scc.h" +#include "hw/misc/mps2-fpgaio.h" +#include "hw/ssi/pl022.h" +#include "hw/i2c/arm_sbcon_i2c.h" #include "hw/net/lan9118.h" #include "net/net.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" typedef enum MPS2FPGAType { FPGA_AN385, @@ -65,8 +69,12 @@ typedef struct { MemoryRegion blockram_m2; MemoryRegion blockram_m3; MemoryRegion sram; + /* FPGA APB subsystem */ MPS2SCC scc; + MPS2FPGAIO fpgaio; + /* CMSDK APB subsystem */ CMSDKAPBDualTimer dualtimer; + CMSDKAPBWatchdog watchdog; } MPS2MachineState; #define TYPE_MPS2_MACHINE "mps2" @@ -111,6 +119,7 @@ static void mps2_common_init(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); MachineClass *mc = MACHINE_GET_CLASS(machine); DeviceState *armv7m, *sccdev; + int i; if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { error_report("This board can only be used with CPU %s", @@ -210,10 +219,11 @@ static void mps2_common_init(MachineState *machine) */ create_unimplemented_device("CMSDK APB peripheral region @0x40000000", 0x40000000, 0x00010000); - create_unimplemented_device("CMSDK peripheral region @0x40010000", + create_unimplemented_device("CMSDK AHB peripheral region @0x40010000", 0x40010000, 0x00010000); create_unimplemented_device("Extra peripheral region @0x40020000", 0x40020000, 0x00010000); + create_unimplemented_device("RESERVED 4", 0x40030000, 0x001D0000); create_unimplemented_device("VGA", 0x41000000, 0x0200000); @@ -225,7 +235,6 @@ static void mps2_common_init(MachineState *machine) */ Object *orgate; DeviceState *orgate_dev; - int i; orgate = object_new(TYPE_OR_IRQ); object_property_set_int(orgate, 6, "num-lines", &error_fatal); @@ -262,7 +271,6 @@ static void mps2_common_init(MachineState *machine) */ Object *orgate; DeviceState *orgate_dev; - int i; orgate = object_new(TYPE_OR_IRQ); object_property_set_int(orgate, 10, "num-lines", &error_fatal); @@ -298,10 +306,15 @@ static void mps2_common_init(MachineState *machine) default: g_assert_not_reached(); } + for (i = 0; i < 4; i++) { + static const hwaddr gpiobase[] = {0x40010000, 0x40011000, + 0x40012000, 0x40013000}; + create_unimplemented_device("cmsdk-ahb-gpio", gpiobase[i], 0x1000); + } + /* CMSDK APB subsystem */ cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ); cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ); - object_initialize_child(OBJECT(mms), "dualtimer", &mms->dualtimer, TYPE_CMSDK_APB_DUALTIMER); qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ); @@ -309,7 +322,15 @@ static void mps2_common_init(MachineState *machine) sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0, qdev_get_gpio_in(armv7m, 10)); sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0x40002000); - + object_initialize_child(OBJECT(mms), "watchdog", &mms->watchdog, + TYPE_CMSDK_APB_WATCHDOG); + qdev_prop_set_uint32(DEVICE(&mms->watchdog), "wdogclk-frq", SYSCLK_FRQ); + sysbus_realize(SYS_BUS_DEVICE(&mms->watchdog), &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->watchdog), 0, + qdev_get_gpio_in_named(armv7m, "NMI", 0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->watchdog), 0, 0x40008000); + + /* FPGA APB subsystem */ object_initialize_child(OBJECT(mms), "scc", &mms->scc, TYPE_MPS2_SCC); sccdev = DEVICE(&mms->scc); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); @@ -317,6 +338,42 @@ static void mps2_common_init(MachineState *machine) qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); sysbus_realize(SYS_BUS_DEVICE(&mms->scc), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(sccdev), 0, 0x4002f000); + object_initialize_child(OBJECT(mms), "fpgaio", + &mms->fpgaio, TYPE_MPS2_FPGAIO); + qdev_prop_set_uint32(DEVICE(&mms->fpgaio), "prescale-clk", 25000000); + sysbus_realize(SYS_BUS_DEVICE(&mms->fpgaio), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->fpgaio), 0, 0x40028000); + sysbus_create_simple(TYPE_PL022, 0x40025000, /* External ADC */ + qdev_get_gpio_in(armv7m, 22)); + for (i = 0; i < 2; i++) { + static const int spi_irqno[] = {11, 24}; + static const hwaddr spibase[] = {0x40020000, /* APB */ + 0x40021000, /* LCD */ + 0x40026000, /* Shield0 */ + 0x40027000}; /* Shield1 */ + DeviceState *orgate_dev; + Object *orgate; + int j; + + orgate = object_new(TYPE_OR_IRQ); + object_property_set_int(orgate, 2, "num-lines", &error_fatal); + orgate_dev = DEVICE(orgate); + qdev_realize(orgate_dev, NULL, &error_fatal); + qdev_connect_gpio_out(orgate_dev, 0, + qdev_get_gpio_in(armv7m, spi_irqno[i])); + for (j = 0; j < 2; j++) { + sysbus_create_simple(TYPE_PL022, spibase[2 * i + j], + qdev_get_gpio_in(orgate_dev, j)); + } + } + for (i = 0; i < 4; i++) { + static const hwaddr i2cbase[] = {0x40022000, /* Touch */ + 0x40023000, /* Audio */ + 0x40029000, /* Shield0 */ + 0x4002a000}; /* Shield1 */ + sysbus_create_simple(TYPE_ARM_SBCON_I2C, i2cbase[i], NULL); + } + create_unimplemented_device("i2s", 0x40024000, 0x400); /* In hardware this is a LAN9220; the LAN9118 is software compatible * except that it doesn't support the checksum-offload feature. diff --git a/hw/arm/realview.c b/hw/arm/realview.c index f3c00fe00c..b6c0a1adb9 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -26,6 +26,7 @@ #include "hw/cpu/a9mpcore.h" #include "hw/intc/realview_gic.h" #include "hw/irq.h" +#include "hw/i2c/arm_sbcon_i2c.h" #define SMP_BOOT_ADDR 0xe0000000 #define SMP_BOOTREG_ADDR 0x10000030 @@ -282,7 +283,7 @@ static void realview_init(MachineState *machine, } } - dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL); + dev = sysbus_create_simple(TYPE_VERSATILE_I2C, 0x10002000, NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_create_slave(i2c, "ds1338", 0x68); diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index 2ebdcbd8ac..e596b8170f 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -18,6 +18,7 @@ #include "sysemu/sysemu.h" #include "hw/pci/pci.h" #include "hw/i2c/i2c.h" +#include "hw/i2c/arm_sbcon_i2c.h" #include "hw/irq.h" #include "hw/boards.h" #include "exec/address-spaces.h" @@ -314,7 +315,7 @@ static void versatile_init(MachineState *machine, int board_id) /* Add PL031 Real Time Clock. */ sysbus_create_simple("pl031", 0x101e8000, pic[10]); - dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL); + dev = sysbus_create_simple(TYPE_VERSATILE_I2C, 0x10002000, NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_create_slave(i2c, "ds1338", 0x68); diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 7ca5d523a4..24d656e653 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -42,6 +42,7 @@ #include "hw/char/pl011.h" #include "hw/cpu/a9mpcore.h" #include "hw/cpu/a15mpcore.h" +#include "hw/i2c/arm_sbcon_i2c.h" #define VEXPRESS_BOARD_ID 0x8e0 #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024) @@ -640,7 +641,7 @@ static void vexpress_common_init(MachineState *machine) sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); - dev = sysbus_create_simple("versatile_i2c", map[VE_SERIALDVI], NULL); + dev = sysbus_create_simple(TYPE_VERSATILE_I2C, map[VE_SERIALDVI], NULL); i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); i2c_create_slave(i2c, "sii9022", 0x39); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index caceb1e4a0..402c362c14 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2177,11 +2177,68 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, } } +static void virt_dimm_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + Error *local_err = NULL; + + if (!vms->acpi_dev) { + error_setg(&local_err, + "memory hotplug is not enabled: missing acpi-ged device"); + goto out; + } + + if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { + error_setg(&local_err, + "nvdimm device hot unplug is not supported yet."); + goto out; + } + + hotplug_handler_unplug_request(HOTPLUG_HANDLER(vms->acpi_dev), dev, + &local_err); +out: + error_propagate(errp, local_err); +} + +static void virt_dimm_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + Error *local_err = NULL; + + hotplug_handler_unplug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err); + if (local_err) { + goto out; + } + + pc_dimm_unplug(PC_DIMM(dev), MACHINE(vms)); + qdev_unrealize(dev); + +out: + error_propagate(errp, local_err); +} + static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - error_setg(errp, "device unplug request for unsupported device" - " type: %s", object_get_typename(OBJECT(dev))); + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + virt_dimm_unplug_request(hotplug_dev, dev, errp); + } else { + error_setg(errp, "device unplug request for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } +} + +static void virt_machine_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + virt_dimm_unplug(hotplug_dev, dev, errp); + } else { + error_setg(errp, "virt: device unplug for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); + } } static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, @@ -2262,6 +2319,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) hc->pre_plug = virt_machine_device_pre_plug_cb; hc->plug = virt_machine_device_plug_cb; hc->unplug_request = virt_machine_device_unplug_request_cb; + hc->unplug = virt_machine_device_unplug_cb; mc->numa_mem_supported = true; mc->nvdimm_supported = true; mc->auto_enable_numa_with_memhp = true; @@ -2375,6 +2433,7 @@ DEFINE_VIRT_MACHINE_AS_LATEST(5, 1) static void virt_machine_5_0_options(MachineClass *mc) { virt_machine_5_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_5_0, hw_compat_5_0_len); } DEFINE_VIRT_MACHINE(5, 0) diff --git a/hw/audio/gusemu_hal.c b/hw/audio/gusemu_hal.c index ae40ca341c..5b9a14ee21 100644 --- a/hw/audio/gusemu_hal.c +++ b/hw/audio/gusemu_hal.c @@ -32,7 +32,7 @@ #define GUSregb(position) (* (gusptr+(position))) #define GUSregw(position) (*(uint16_t *) (gusptr+(position))) -#define GUSregd(position) (*(uint16_t *)(gusptr+(position))) +#define GUSregd(position) (*(uint32_t *)(gusptr + (position))) /* size given in bytes */ unsigned int gus_read(GUSEmuState * state, int port, int size) diff --git a/hw/audio/gusemu_mixer.c b/hw/audio/gusemu_mixer.c index 00b9861b92..56300de77e 100644 --- a/hw/audio/gusemu_mixer.c +++ b/hw/audio/gusemu_mixer.c @@ -28,7 +28,7 @@ #define GUSregb(position) (* (gusptr+(position))) #define GUSregw(position) (*(uint16_t *) (gusptr+(position))) -#define GUSregd(position) (*(uint16_t *)(gusptr+(position))) +#define GUSregd(position) (*(uint32_t *)(gusptr + (position))) #define GUSvoice(position) (*(uint16_t *)(voiceptr+(position))) diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 40e7a8b8bb..874627520c 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -46,3 +46,6 @@ config SCLPCONSOLE config TERMINAL3270 bool + +config RENESAS_SCI + bool diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 9e9a6c1aff..8306c4a789 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -12,6 +12,7 @@ common-obj-$(CONFIG_VIRTIO_SERIAL) += virtio-console.o common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o common-obj-$(CONFIG_XEN) += xen_console.o common-obj-$(CONFIG_CADENCE) += cadence_uart.o +common-obj-$(CONFIG_IBEX) += ibex_uart.o common-obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o common-obj-$(CONFIG_COLDFIRE) += mcf_uart.o @@ -20,6 +21,7 @@ common-obj-$(CONFIG_SH4) += sh_serial.o common-obj-$(CONFIG_DIGIC) += digic-uart.o common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o common-obj-$(CONFIG_RASPI) += bcm2835_aux.o +common-obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c new file mode 100644 index 0000000000..3e0dd9968e --- /dev/null +++ b/hw/char/ibex_uart.c @@ -0,0 +1,492 @@ +/* + * QEMU lowRISC Ibex UART device + * + * Copyright (c) 2020 Western Digital + * + * For details check the documentation here: + * https://docs.opentitan.org/hw/ip/uart/doc/ + * + * 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/char/ibex_uart.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" + +static void ibex_uart_update_irqs(IbexUartState *s) +{ + if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_TX_WATERMARK) { + qemu_set_irq(s->tx_watermark, 1); + } else { + qemu_set_irq(s->tx_watermark, 0); + } + + if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_RX_WATERMARK) { + qemu_set_irq(s->rx_watermark, 1); + } else { + qemu_set_irq(s->rx_watermark, 0); + } + + if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_TX_EMPTY) { + qemu_set_irq(s->tx_empty, 1); + } else { + qemu_set_irq(s->tx_empty, 0); + } + + if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_RX_OVERFLOW) { + qemu_set_irq(s->rx_overflow, 1); + } else { + qemu_set_irq(s->rx_overflow, 0); + } +} + +static int ibex_uart_can_receive(void *opaque) +{ + IbexUartState *s = opaque; + + if (s->uart_ctrl & UART_CTRL_RX_ENABLE) { + return 1; + } + + return 0; +} + +static void ibex_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + IbexUartState *s = opaque; + uint8_t rx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_RXILVL) + >> FIFO_CTRL_RXILVL_SHIFT; + + s->uart_rdata = *buf; + + s->uart_status &= ~UART_STATUS_RXIDLE; + s->uart_status &= ~UART_STATUS_RXEMPTY; + + if (size > rx_fifo_level) { + s->uart_intr_state |= INTR_STATE_RX_WATERMARK; + } + + ibex_uart_update_irqs(s); +} + +static gboolean ibex_uart_xmit(GIOChannel *chan, GIOCondition cond, + void *opaque) +{ + IbexUartState *s = opaque; + uint8_t tx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_TXILVL) + >> FIFO_CTRL_TXILVL_SHIFT; + int ret; + + /* instant drain the fifo when there's no back-end */ + if (!qemu_chr_fe_backend_connected(&s->chr)) { + s->tx_level = 0; + return FALSE; + } + + if (!s->tx_level) { + s->uart_status &= ~UART_STATUS_TXFULL; + s->uart_status |= UART_STATUS_TXEMPTY; + s->uart_intr_state |= INTR_STATE_TX_EMPTY; + s->uart_intr_state &= ~INTR_STATE_TX_WATERMARK; + ibex_uart_update_irqs(s); + return FALSE; + } + + ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level); + + if (ret >= 0) { + s->tx_level -= ret; + memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level); + } + + if (s->tx_level) { + guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + ibex_uart_xmit, s); + if (!r) { + s->tx_level = 0; + return FALSE; + } + } + + /* Clear the TX Full bit */ + if (s->tx_level != IBEX_UART_TX_FIFO_SIZE) { + s->uart_status &= ~UART_STATUS_TXFULL; + } + + /* Disable the TX_WATERMARK IRQ */ + if (s->tx_level < tx_fifo_level) { + s->uart_intr_state &= ~INTR_STATE_TX_WATERMARK; + } + + /* Set TX empty */ + if (s->tx_level == 0) { + s->uart_status |= UART_STATUS_TXEMPTY; + s->uart_intr_state |= INTR_STATE_TX_EMPTY; + } + + ibex_uart_update_irqs(s); + return FALSE; +} + +static void uart_write_tx_fifo(IbexUartState *s, const uint8_t *buf, + int size) +{ + uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint8_t tx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_TXILVL) + >> FIFO_CTRL_TXILVL_SHIFT; + + if (size > IBEX_UART_TX_FIFO_SIZE - s->tx_level) { + size = IBEX_UART_TX_FIFO_SIZE - s->tx_level; + qemu_log_mask(LOG_GUEST_ERROR, "ibex_uart: TX FIFO overflow"); + } + + memcpy(s->tx_fifo + s->tx_level, buf, size); + s->tx_level += size; + + if (s->tx_level > 0) { + s->uart_status &= ~UART_STATUS_TXEMPTY; + } + + if (s->tx_level >= tx_fifo_level) { + s->uart_intr_state |= INTR_STATE_TX_WATERMARK; + ibex_uart_update_irqs(s); + } + + if (s->tx_level == IBEX_UART_TX_FIFO_SIZE) { + s->uart_status |= UART_STATUS_TXFULL; + } + + timer_mod(s->fifo_trigger_handle, current_time + + (s->char_tx_time * 4)); +} + +static void ibex_uart_reset(DeviceState *dev) +{ + IbexUartState *s = IBEX_UART(dev); + + s->uart_intr_state = 0x00000000; + s->uart_intr_state = 0x00000000; + s->uart_intr_enable = 0x00000000; + s->uart_ctrl = 0x00000000; + s->uart_status = 0x0000003c; + s->uart_rdata = 0x00000000; + s->uart_fifo_ctrl = 0x00000000; + s->uart_fifo_status = 0x00000000; + s->uart_ovrd = 0x00000000; + s->uart_val = 0x00000000; + s->uart_timeout_ctrl = 0x00000000; + + s->tx_level = 0; + + s->char_tx_time = (NANOSECONDS_PER_SECOND / 230400) * 10; + + ibex_uart_update_irqs(s); +} + +static uint64_t ibex_uart_read(void *opaque, hwaddr addr, + unsigned int size) +{ + IbexUartState *s = opaque; + uint64_t retvalue = 0; + + switch (addr) { + case IBEX_UART_INTR_STATE: + retvalue = s->uart_intr_state; + break; + case IBEX_UART_INTR_ENABLE: + retvalue = s->uart_intr_enable; + break; + case IBEX_UART_INTR_TEST: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: wdata is write only\n", __func__); + break; + + case IBEX_UART_CTRL: + retvalue = s->uart_ctrl; + break; + case IBEX_UART_STATUS: + retvalue = s->uart_status; + break; + + case IBEX_UART_RDATA: + retvalue = s->uart_rdata; + if (s->uart_ctrl & UART_CTRL_RX_ENABLE) { + qemu_chr_fe_accept_input(&s->chr); + + s->uart_status |= UART_STATUS_RXIDLE; + s->uart_status |= UART_STATUS_RXEMPTY; + } + break; + case IBEX_UART_WDATA: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: wdata is write only\n", __func__); + break; + + case IBEX_UART_FIFO_CTRL: + retvalue = s->uart_fifo_ctrl; + break; + case IBEX_UART_FIFO_STATUS: + retvalue = s->uart_fifo_status; + + retvalue |= s->tx_level & 0x1F; + + qemu_log_mask(LOG_UNIMP, + "%s: RX fifos are not supported\n", __func__); + break; + + case IBEX_UART_OVRD: + retvalue = s->uart_ovrd; + qemu_log_mask(LOG_UNIMP, + "%s: ovrd is not supported\n", __func__); + break; + case IBEX_UART_VAL: + retvalue = s->uart_val; + qemu_log_mask(LOG_UNIMP, + "%s: val is not supported\n", __func__); + break; + case IBEX_UART_TIMEOUT_CTRL: + retvalue = s->uart_timeout_ctrl; + qemu_log_mask(LOG_UNIMP, + "%s: timeout_ctrl is not supported\n", __func__); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + return 0; + } + + return retvalue; +} + +static void ibex_uart_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + IbexUartState *s = opaque; + uint32_t value = val64; + + switch (addr) { + case IBEX_UART_INTR_STATE: + /* Write 1 clear */ + s->uart_intr_state &= ~value; + ibex_uart_update_irqs(s); + break; + case IBEX_UART_INTR_ENABLE: + s->uart_intr_enable = value; + ibex_uart_update_irqs(s); + break; + case IBEX_UART_INTR_TEST: + s->uart_intr_state |= value; + ibex_uart_update_irqs(s); + break; + + case IBEX_UART_CTRL: + s->uart_ctrl = value; + + if (value & UART_CTRL_NF) { + qemu_log_mask(LOG_UNIMP, + "%s: UART_CTRL_NF is not supported\n", __func__); + } + if (value & UART_CTRL_SLPBK) { + qemu_log_mask(LOG_UNIMP, + "%s: UART_CTRL_SLPBK is not supported\n", __func__); + } + if (value & UART_CTRL_LLPBK) { + qemu_log_mask(LOG_UNIMP, + "%s: UART_CTRL_LLPBK is not supported\n", __func__); + } + if (value & UART_CTRL_PARITY_EN) { + qemu_log_mask(LOG_UNIMP, + "%s: UART_CTRL_PARITY_EN is not supported\n", + __func__); + } + if (value & UART_CTRL_PARITY_ODD) { + qemu_log_mask(LOG_UNIMP, + "%s: UART_CTRL_PARITY_ODD is not supported\n", + __func__); + } + if (value & UART_CTRL_RXBLVL) { + qemu_log_mask(LOG_UNIMP, + "%s: UART_CTRL_RXBLVL is not supported\n", __func__); + } + if (value & UART_CTRL_NCO) { + uint64_t baud = ((value & UART_CTRL_NCO) >> 16); + baud *= 1000; + baud /= 2 ^ 20; + + s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10; + } + break; + case IBEX_UART_STATUS: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: status is read only\n", __func__); + break; + + case IBEX_UART_RDATA: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: rdata is read only\n", __func__); + break; + case IBEX_UART_WDATA: + uart_write_tx_fifo(s, (uint8_t *) &value, 1); + break; + + case IBEX_UART_FIFO_CTRL: + s->uart_fifo_ctrl = value; + + if (value & FIFO_CTRL_RXRST) { + qemu_log_mask(LOG_UNIMP, + "%s: RX fifos are not supported\n", __func__); + } + if (value & FIFO_CTRL_TXRST) { + s->tx_level = 0; + } + break; + case IBEX_UART_FIFO_STATUS: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: fifo_status is read only\n", __func__); + break; + + case IBEX_UART_OVRD: + s->uart_ovrd = value; + qemu_log_mask(LOG_UNIMP, + "%s: ovrd is not supported\n", __func__); + break; + case IBEX_UART_VAL: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: val is read only\n", __func__); + break; + case IBEX_UART_TIMEOUT_CTRL: + s->uart_timeout_ctrl = value; + qemu_log_mask(LOG_UNIMP, + "%s: timeout_ctrl is not supported\n", __func__); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static void fifo_trigger_update(void *opaque) +{ + IbexUartState *s = opaque; + + if (s->uart_ctrl & UART_CTRL_TX_ENABLE) { + ibex_uart_xmit(NULL, G_IO_OUT, s); + } +} + +static const MemoryRegionOps ibex_uart_ops = { + .read = ibex_uart_read, + .write = ibex_uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static int ibex_uart_post_load(void *opaque, int version_id) +{ + IbexUartState *s = opaque; + + ibex_uart_update_irqs(s); + return 0; +} + +static const VMStateDescription vmstate_ibex_uart = { + .name = TYPE_IBEX_UART, + .version_id = 1, + .minimum_version_id = 1, + .post_load = ibex_uart_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(tx_fifo, IbexUartState, + IBEX_UART_TX_FIFO_SIZE), + VMSTATE_UINT32(tx_level, IbexUartState), + VMSTATE_UINT64(char_tx_time, IbexUartState), + VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexUartState), + VMSTATE_UINT32(uart_intr_state, IbexUartState), + VMSTATE_UINT32(uart_intr_enable, IbexUartState), + VMSTATE_UINT32(uart_ctrl, IbexUartState), + VMSTATE_UINT32(uart_status, IbexUartState), + VMSTATE_UINT32(uart_rdata, IbexUartState), + VMSTATE_UINT32(uart_fifo_ctrl, IbexUartState), + VMSTATE_UINT32(uart_fifo_status, IbexUartState), + VMSTATE_UINT32(uart_ovrd, IbexUartState), + VMSTATE_UINT32(uart_val, IbexUartState), + VMSTATE_UINT32(uart_timeout_ctrl, IbexUartState), + VMSTATE_END_OF_LIST() + } +}; + +static Property ibex_uart_properties[] = { + DEFINE_PROP_CHR("chardev", IbexUartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ibex_uart_init(Object *obj) +{ + IbexUartState *s = IBEX_UART(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_watermark); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_empty); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_overflow); + + memory_region_init_io(&s->mmio, obj, &ibex_uart_ops, s, + TYPE_IBEX_UART, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void ibex_uart_realize(DeviceState *dev, Error **errp) +{ + IbexUartState *s = IBEX_UART(dev); + + s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, + fifo_trigger_update, s); + + qemu_chr_fe_set_handlers(&s->chr, ibex_uart_can_receive, + ibex_uart_receive, NULL, NULL, + s, NULL, true); +} + +static void ibex_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = ibex_uart_reset; + dc->realize = ibex_uart_realize; + dc->vmsd = &vmstate_ibex_uart; + device_class_set_props(dc, ibex_uart_properties); +} + +static const TypeInfo ibex_uart_info = { + .name = TYPE_IBEX_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IbexUartState), + .instance_init = ibex_uart_init, + .class_init = ibex_uart_class_init, +}; + +static void ibex_uart_register_types(void) +{ + type_register_static(&ibex_uart_info); +} + +type_init(ibex_uart_register_types) diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c new file mode 100644 index 0000000000..5d7c6e6523 --- /dev/null +++ b/hw/char/renesas_sci.c @@ -0,0 +1,350 @@ +/* + * Renesas Serial Communication Interface + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/char/renesas_sci.h" +#include "migration/vmstate.h" + +/* SCI register map */ +REG8(SMR, 0) + FIELD(SMR, CKS, 0, 2) + FIELD(SMR, MP, 2, 1) + FIELD(SMR, STOP, 3, 1) + FIELD(SMR, PM, 4, 1) + FIELD(SMR, PE, 5, 1) + FIELD(SMR, CHR, 6, 1) + FIELD(SMR, CM, 7, 1) +REG8(BRR, 1) +REG8(SCR, 2) + FIELD(SCR, CKE, 0, 2) + FIELD(SCR, TEIE, 2, 1) + FIELD(SCR, MPIE, 3, 1) + FIELD(SCR, RE, 4, 1) + FIELD(SCR, TE, 5, 1) + FIELD(SCR, RIE, 6, 1) + FIELD(SCR, TIE, 7, 1) +REG8(TDR, 3) +REG8(SSR, 4) + FIELD(SSR, MPBT, 0, 1) + FIELD(SSR, MPB, 1, 1) + FIELD(SSR, TEND, 2, 1) + FIELD(SSR, ERR, 3, 3) + FIELD(SSR, PER, 3, 1) + FIELD(SSR, FER, 4, 1) + FIELD(SSR, ORER, 5, 1) + FIELD(SSR, RDRF, 6, 1) + FIELD(SSR, TDRE, 7, 1) +REG8(RDR, 5) +REG8(SCMR, 6) + FIELD(SCMR, SMIF, 0, 1) + FIELD(SCMR, SINV, 2, 1) + FIELD(SCMR, SDIR, 3, 1) + FIELD(SCMR, BCP2, 7, 1) +REG8(SEMR, 7) + FIELD(SEMR, ACS0, 0, 1) + FIELD(SEMR, ABCS, 4, 1) + +static int can_receive(void *opaque) +{ + RSCIState *sci = RSCI(opaque); + if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { + return 0; + } else { + return FIELD_EX8(sci->scr, SCR, RE); + } +} + +static void receive(void *opaque, const uint8_t *buf, int size) +{ + RSCIState *sci = RSCI(opaque); + sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime; + if (FIELD_EX8(sci->ssr, SSR, RDRF) || size > 1) { + sci->ssr = FIELD_DP8(sci->ssr, SSR, ORER, 1); + if (FIELD_EX8(sci->scr, SCR, RIE)) { + qemu_set_irq(sci->irq[ERI], 1); + } + } else { + sci->rdr = buf[0]; + sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 1); + if (FIELD_EX8(sci->scr, SCR, RIE)) { + qemu_irq_pulse(sci->irq[RXI]); + } + } +} + +static void send_byte(RSCIState *sci) +{ + if (qemu_chr_fe_backend_connected(&sci->chr)) { + qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1); + } + timer_mod(&sci->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime); + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 0); + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1); + qemu_set_irq(sci->irq[TEI], 0); + if (FIELD_EX8(sci->scr, SCR, TIE)) { + qemu_irq_pulse(sci->irq[TXI]); + } +} + +static void txend(void *opaque) +{ + RSCIState *sci = RSCI(opaque); + if (!FIELD_EX8(sci->ssr, SSR, TDRE)) { + send_byte(sci); + } else { + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1); + if (FIELD_EX8(sci->scr, SCR, TEIE)) { + qemu_set_irq(sci->irq[TEI], 1); + } + } +} + +static void update_trtime(RSCIState *sci) +{ + /* char per bits */ + sci->trtime = 8 - FIELD_EX8(sci->smr, SMR, CHR); + sci->trtime += FIELD_EX8(sci->smr, SMR, PE); + sci->trtime += FIELD_EX8(sci->smr, SMR, STOP) + 1; + /* x bit transmit time (32 * divrate * brr) / base freq */ + sci->trtime *= 32 * sci->brr; + sci->trtime *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS)); + sci->trtime *= NANOSECONDS_PER_SECOND; + sci->trtime /= sci->input_freq; +} + +static bool sci_is_tr_enabled(RSCIState *sci) +{ + return FIELD_EX8(sci->scr, SCR, TE) || FIELD_EX8(sci->scr, SCR, RE); +} + +static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + RSCIState *sci = RSCI(opaque); + + switch (offset) { + case A_SMR: + if (!sci_is_tr_enabled(sci)) { + sci->smr = val; + update_trtime(sci); + } + break; + case A_BRR: + if (!sci_is_tr_enabled(sci)) { + sci->brr = val; + update_trtime(sci); + } + break; + case A_SCR: + sci->scr = val; + if (FIELD_EX8(sci->scr, SCR, TE)) { + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1); + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1); + if (FIELD_EX8(sci->scr, SCR, TIE)) { + qemu_irq_pulse(sci->irq[TXI]); + } + } + if (!FIELD_EX8(sci->scr, SCR, TEIE)) { + qemu_set_irq(sci->irq[TEI], 0); + } + if (!FIELD_EX8(sci->scr, SCR, RIE)) { + qemu_set_irq(sci->irq[ERI], 0); + } + break; + case A_TDR: + sci->tdr = val; + if (FIELD_EX8(sci->ssr, SSR, TEND)) { + send_byte(sci); + } else { + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0); + } + break; + case A_SSR: + sci->ssr = FIELD_DP8(sci->ssr, SSR, MPBT, + FIELD_EX8(val, SSR, MPBT)); + sci->ssr = FIELD_DP8(sci->ssr, SSR, ERR, + FIELD_EX8(val, SSR, ERR) & 0x07); + if (FIELD_EX8(sci->read_ssr, SSR, ERR) && + FIELD_EX8(sci->ssr, SSR, ERR) == 0) { + qemu_set_irq(sci->irq[ERI], 0); + } + break; + case A_RDR: + qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: RDR is read only.\n"); + break; + case A_SCMR: + sci->scmr = val; break; + case A_SEMR: /* SEMR */ + sci->semr = val; break; + default: + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX " " + "not implemented\n", + offset); + } +} + +static uint64_t sci_read(void *opaque, hwaddr offset, unsigned size) +{ + RSCIState *sci = RSCI(opaque); + + switch (offset) { + case A_SMR: + return sci->smr; + case A_BRR: + return sci->brr; + case A_SCR: + return sci->scr; + case A_TDR: + return sci->tdr; + case A_SSR: + sci->read_ssr = sci->ssr; + return sci->ssr; + case A_RDR: + sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 0); + return sci->rdr; + case A_SCMR: + return sci->scmr; + case A_SEMR: + return sci->semr; + default: + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX + " not implemented.\n", offset); + } + return UINT64_MAX; +} + +static const MemoryRegionOps sci_ops = { + .write = sci_write, + .read = sci_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.max_access_size = 1, + .valid.max_access_size = 1, +}; + +static void rsci_reset(DeviceState *dev) +{ + RSCIState *sci = RSCI(dev); + sci->smr = sci->scr = 0x00; + sci->brr = 0xff; + sci->tdr = 0xff; + sci->rdr = 0x00; + sci->ssr = 0x84; + sci->scmr = 0x00; + sci->semr = 0x00; + sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void sci_event(void *opaque, QEMUChrEvent event) +{ + RSCIState *sci = RSCI(opaque); + if (event == CHR_EVENT_BREAK) { + sci->ssr = FIELD_DP8(sci->ssr, SSR, FER, 1); + if (FIELD_EX8(sci->scr, SCR, RIE)) { + qemu_set_irq(sci->irq[ERI], 1); + } + } +} + +static void rsci_realize(DeviceState *dev, Error **errp) +{ + RSCIState *sci = RSCI(dev); + + if (sci->input_freq == 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "renesas_sci: input-freq property must be set."); + return; + } + qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive, + sci_event, NULL, sci, NULL, true); +} + +static void rsci_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RSCIState *sci = RSCI(obj); + int i; + + memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops, + sci, "renesas-sci", 0x8); + sysbus_init_mmio(d, &sci->memory); + + for (i = 0; i < SCI_NR_IRQ; i++) { + sysbus_init_irq(d, &sci->irq[i]); + } + timer_init_ns(&sci->timer, QEMU_CLOCK_VIRTUAL, txend, sci); +} + +static const VMStateDescription vmstate_rsci = { + .name = "renesas-sci", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(trtime, RSCIState), + VMSTATE_INT64(rx_next, RSCIState), + VMSTATE_UINT8(smr, RSCIState), + VMSTATE_UINT8(brr, RSCIState), + VMSTATE_UINT8(scr, RSCIState), + VMSTATE_UINT8(tdr, RSCIState), + VMSTATE_UINT8(ssr, RSCIState), + VMSTATE_UINT8(rdr, RSCIState), + VMSTATE_UINT8(scmr, RSCIState), + VMSTATE_UINT8(semr, RSCIState), + VMSTATE_UINT8(read_ssr, RSCIState), + VMSTATE_TIMER(timer, RSCIState), + VMSTATE_END_OF_LIST() + } +}; + +static Property rsci_properties[] = { + DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0), + DEFINE_PROP_CHR("chardev", RSCIState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rsci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rsci_realize; + dc->vmsd = &vmstate_rsci; + dc->reset = rsci_reset; + device_class_set_props(dc, rsci_properties); +} + +static const TypeInfo rsci_info = { + .name = TYPE_RENESAS_SCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RSCIState), + .instance_init = rsci_init, + .class_init = rsci_class_init, +}; + +static void rsci_register_types(void) +{ + type_register_static(&rsci_info); +} + +type_init(rsci_register_types) diff --git a/hw/i2c/versatile_i2c.c b/hw/i2c/versatile_i2c.c index 1ac2a6f59a..da8cda2ec1 100644 --- a/hw/i2c/versatile_i2c.c +++ b/hw/i2c/versatile_i2c.c @@ -1,5 +1,6 @@ /* - * ARM Versatile I2C controller + * ARM SBCon two-wire serial bus interface (I2C bitbang) + * a.k.a. ARM Versatile I2C controller * * Copyright (c) 2006-2007 CodeSourcery. * Copyright (c) 2012 Oskar Andero <oskar.andero@gmail.com> @@ -22,32 +23,33 @@ */ #include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/i2c/bitbang_i2c.h" +#include "hw/i2c/arm_sbcon_i2c.h" +#include "hw/registerfields.h" #include "qemu/log.h" #include "qemu/module.h" -#define TYPE_VERSATILE_I2C "versatile_i2c" #define VERSATILE_I2C(obj) \ OBJECT_CHECK(VersatileI2CState, (obj), TYPE_VERSATILE_I2C) -typedef struct VersatileI2CState { - SysBusDevice parent_obj; +typedef ArmSbconI2CState VersatileI2CState; - MemoryRegion iomem; - bitbang_i2c_interface bitbang; - int out; - int in; -} VersatileI2CState; + +REG32(CONTROL_GET, 0) +REG32(CONTROL_SET, 0) +REG32(CONTROL_CLR, 4) + +#define SCL BIT(0) +#define SDA BIT(1) static uint64_t versatile_i2c_read(void *opaque, hwaddr offset, unsigned size) { VersatileI2CState *s = (VersatileI2CState *)opaque; - if (offset == 0) { + switch (offset) { + case A_CONTROL_SET: return (s->out & 1) | (s->in << 1); - } else { + default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%x\n", __func__, (int)offset); return -1; @@ -60,18 +62,18 @@ static void versatile_i2c_write(void *opaque, hwaddr offset, VersatileI2CState *s = (VersatileI2CState *)opaque; switch (offset) { - case 0: + case A_CONTROL_SET: s->out |= value & 3; break; - case 4: + case A_CONTROL_CLR: s->out &= ~value; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%x\n", __func__, (int)offset); } - bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & 1) != 0); - s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & 2) != 0); + bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & SCL) != 0); + s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & SDA) != 0); } static const MemoryRegionOps versatile_i2c_ops = { @@ -90,7 +92,7 @@ static void versatile_i2c_init(Object *obj) bus = i2c_init_bus(dev, "i2c"); bitbang_i2c_init(&s->bitbang, bus); memory_region_init_io(&s->iomem, obj, &versatile_i2c_ops, s, - "versatile_i2c", 0x1000); + "arm_sbcon_i2c", 0x1000); sysbus_init_mmio(sbd, &s->iomem); } diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index a189d6fedd..f562342bab 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -61,3 +61,6 @@ config S390_FLIC_KVM config OMPIC bool + +config RX_ICU + bool diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index f726d87532..a4202636d4 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -20,6 +20,7 @@ common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_dist.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_redist.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_its_common.o common-obj-$(CONFIG_OPENPIC) += openpic.o +common-obj-$(CONFIG_RX_ICU) += rx_icu.o common-obj-y += intc.o obj-$(CONFIG_APIC) += apic.o apic_common.o @@ -49,3 +50,4 @@ obj-$(CONFIG_ARM_GIC) += arm_gicv3_cpuif.o obj-$(CONFIG_MIPS_CPS) += mips_gic.o obj-$(CONFIG_NIOS2) += nios2_iic.o obj-$(CONFIG_OMPIC) += ompic.o +obj-$(CONFIG_IBEX) += ibex_plic.o diff --git a/hw/intc/ibex_plic.c b/hw/intc/ibex_plic.c new file mode 100644 index 0000000000..41079518c6 --- /dev/null +++ b/hw/intc/ibex_plic.c @@ -0,0 +1,261 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#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" + +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; + + s->pending[pending_num] |= level << (irq % 32); +} + +static bool ibex_plic_irqs_pending(IbexPlicState *s, uint32_t context) +{ + int i; + + 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] > s->threshold) { + if (!s->claim) { + s->claim = irq_num; + } + return true; + } + } + + return false; +} + +static void ibex_plic_update(IbexPlicState *s) +{ + CPUState *cpu; + int level, i; + + for (i = 0; i < s->num_cpus; i++) { + cpu = qemu_get_cpu(i); + + if (!cpu) { + continue; + } + + level = ibex_plic_irqs_pending(s, 0); + + riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level)); + } +} + +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)); + + ret = s->claim; + } + + 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; + } 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; + } + } + + 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, 80), + + DEFINE_PROP_UINT32("pending-base", IbexPlicState, pending_base, 0), + DEFINE_PROP_UINT32("pending-num", IbexPlicState, pending_num, 3), + + DEFINE_PROP_UINT32("source-base", IbexPlicState, source_base, 0x0c), + DEFINE_PROP_UINT32("source-num", IbexPlicState, source_num, 3), + + DEFINE_PROP_UINT32("priority-base", IbexPlicState, priority_base, 0x18), + DEFINE_PROP_UINT32("priority-num", IbexPlicState, priority_num, 80), + + DEFINE_PROP_UINT32("enable-base", IbexPlicState, enable_base, 0x200), + DEFINE_PROP_UINT32("enable-num", IbexPlicState, enable_num, 3), + + DEFINE_PROP_UINT32("threshold-base", IbexPlicState, threshold_base, 0x20c), + + DEFINE_PROP_UINT32("claim-base", IbexPlicState, claim_base, 0x210), + 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->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); + + /* + * 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/rx_icu.c b/hw/intc/rx_icu.c new file mode 100644 index 0000000000..df4b6a8d22 --- /dev/null +++ b/hw/intc/rx_icu.c @@ -0,0 +1,397 @@ +/* + * RX Interrupt Control Unit + * + * Warning: Only ICUa is supported. + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/intc/rx_icu.h" +#include "migration/vmstate.h" + +REG8(IR, 0) + FIELD(IR, IR, 0, 1) +REG8(DTCER, 0x100) + FIELD(DTCER, DTCE, 0, 1) +REG8(IER, 0x200) +REG8(SWINTR, 0x2e0) + FIELD(SWINTR, SWINT, 0, 1) +REG16(FIR, 0x2f0) + FIELD(FIR, FVCT, 0, 8) + FIELD(FIR, FIEN, 15, 1) +REG8(IPR, 0x300) + FIELD(IPR, IPR, 0, 4) +REG8(DMRSR, 0x400) +REG8(IRQCR, 0x500) + FIELD(IRQCR, IRQMD, 2, 2) +REG8(NMISR, 0x580) + FIELD(NMISR, NMIST, 0, 1) + FIELD(NMISR, LVDST, 1, 1) + FIELD(NMISR, OSTST, 2, 1) +REG8(NMIER, 0x581) + FIELD(NMIER, NMIEN, 0, 1) + FIELD(NMIER, LVDEN, 1, 1) + FIELD(NMIER, OSTEN, 2, 1) +REG8(NMICLR, 0x582) + FIELD(NMICLR, NMICLR, 0, 1) + FIELD(NMICLR, OSTCLR, 2, 1) +REG8(NMICR, 0x583) + FIELD(NMICR, NMIMD, 3, 1) + +static void set_irq(RXICUState *icu, int n_IRQ, int req) +{ + if ((icu->fir & R_FIR_FIEN_MASK) && + (icu->fir & R_FIR_FVCT_MASK) == n_IRQ) { + qemu_set_irq(icu->_fir, req); + } else { + qemu_set_irq(icu->_irq, req); + } +} + +static uint16_t rxicu_level(RXICUState *icu, unsigned n) +{ + return (icu->ipr[icu->map[n]] << 8) | n; +} + +static void rxicu_request(RXICUState *icu, int n_IRQ) +{ + int enable; + + enable = icu->ier[n_IRQ / 8] & (1 << (n_IRQ & 7)); + if (n_IRQ > 0 && enable != 0 && atomic_read(&icu->req_irq) < 0) { + atomic_set(&icu->req_irq, n_IRQ); + set_irq(icu, n_IRQ, rxicu_level(icu, n_IRQ)); + } +} + +static void rxicu_set_irq(void *opaque, int n_IRQ, int level) +{ + RXICUState *icu = opaque; + struct IRQSource *src; + int issue; + + if (n_IRQ >= NR_IRQS) { + error_report("%s: IRQ %d out of range", __func__, n_IRQ); + return; + } + + src = &icu->src[n_IRQ]; + + level = (level != 0); + switch (src->sense) { + case TRG_LEVEL: + /* level-sensitive irq */ + issue = level; + src->level = level; + break; + case TRG_NEDGE: + issue = (level == 0 && src->level == 1); + src->level = level; + break; + case TRG_PEDGE: + issue = (level == 1 && src->level == 0); + src->level = level; + break; + case TRG_BEDGE: + issue = ((level ^ src->level) & 1); + src->level = level; + break; + default: + g_assert_not_reached(); + } + if (issue == 0 && src->sense == TRG_LEVEL) { + icu->ir[n_IRQ] = 0; + if (atomic_read(&icu->req_irq) == n_IRQ) { + /* clear request */ + set_irq(icu, n_IRQ, 0); + atomic_set(&icu->req_irq, -1); + } + return; + } + if (issue) { + icu->ir[n_IRQ] = 1; + rxicu_request(icu, n_IRQ); + } +} + +static void rxicu_ack_irq(void *opaque, int no, int level) +{ + RXICUState *icu = opaque; + int i; + int n_IRQ; + int max_pri; + + n_IRQ = atomic_read(&icu->req_irq); + if (n_IRQ < 0) { + return; + } + atomic_set(&icu->req_irq, -1); + if (icu->src[n_IRQ].sense != TRG_LEVEL) { + icu->ir[n_IRQ] = 0; + } + + max_pri = 0; + n_IRQ = -1; + for (i = 0; i < NR_IRQS; i++) { + if (icu->ir[i]) { + if (max_pri < icu->ipr[icu->map[i]]) { + n_IRQ = i; + max_pri = icu->ipr[icu->map[i]]; + } + } + } + + if (n_IRQ >= 0) { + rxicu_request(icu, n_IRQ); + } +} + +static uint64_t icu_read(void *opaque, hwaddr addr, unsigned size) +{ + RXICUState *icu = opaque; + int reg = addr & 0xff; + + if ((addr != A_FIR && size != 1) || + (addr == A_FIR && size != 2)) { + qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid read size 0x%" + HWADDR_PRIX "\n", + addr); + return UINT64_MAX; + } + switch (addr) { + case A_IR ... A_IR + 0xff: + return icu->ir[reg] & R_IR_IR_MASK; + case A_DTCER ... A_DTCER + 0xff: + return icu->dtcer[reg] & R_DTCER_DTCE_MASK; + case A_IER ... A_IER + 0x1f: + return icu->ier[reg]; + case A_SWINTR: + return 0; + case A_FIR: + return icu->fir & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK); + case A_IPR ... A_IPR + 0x8f: + return icu->ipr[reg] & R_IPR_IPR_MASK; + case A_DMRSR: + case A_DMRSR + 4: + case A_DMRSR + 8: + case A_DMRSR + 12: + return icu->dmasr[reg >> 2]; + case A_IRQCR ... A_IRQCR + 0x1f: + return icu->src[64 + reg].sense << R_IRQCR_IRQMD_SHIFT; + case A_NMISR: + case A_NMICLR: + return 0; + case A_NMIER: + return icu->nmier; + case A_NMICR: + return icu->nmicr; + default: + qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " " + "not implemented.\n", + addr); + break; + } + return UINT64_MAX; +} + +static void icu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RXICUState *icu = opaque; + int reg = addr & 0xff; + + if ((addr != A_FIR && size != 1) || + (addr == A_FIR && size != 2)) { + qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid write size at " + "0x%" HWADDR_PRIX "\n", + addr); + return; + } + switch (addr) { + case A_IR ... A_IR + 0xff: + if (icu->src[reg].sense != TRG_LEVEL && val == 0) { + icu->ir[reg] = 0; + } + break; + case A_DTCER ... A_DTCER + 0xff: + icu->dtcer[reg] = val & R_DTCER_DTCE_MASK; + qemu_log_mask(LOG_UNIMP, "rx_icu: DTC not implemented\n"); + break; + case A_IER ... A_IER + 0x1f: + icu->ier[reg] = val; + break; + case A_SWINTR: + if (val & R_SWINTR_SWINT_MASK) { + qemu_irq_pulse(icu->_swi); + } + break; + case A_FIR: + icu->fir = val & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK); + break; + case A_IPR ... A_IPR + 0x8f: + icu->ipr[reg] = val & R_IPR_IPR_MASK; + break; + case A_DMRSR: + case A_DMRSR + 4: + case A_DMRSR + 8: + case A_DMRSR + 12: + icu->dmasr[reg >> 2] = val; + qemu_log_mask(LOG_UNIMP, "rx_icu: DMAC not implemented\n"); + break; + case A_IRQCR ... A_IRQCR + 0x1f: + icu->src[64 + reg].sense = val >> R_IRQCR_IRQMD_SHIFT; + break; + case A_NMICLR: + break; + case A_NMIER: + icu->nmier |= val & (R_NMIER_NMIEN_MASK | + R_NMIER_LVDEN_MASK | + R_NMIER_OSTEN_MASK); + break; + case A_NMICR: + if ((icu->nmier & R_NMIER_NMIEN_MASK) == 0) { + icu->nmicr = val & R_NMICR_NMIMD_MASK; + } + break; + default: + qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " " + "not implemented\n", + addr); + break; + } +} + +static const MemoryRegionOps icu_ops = { + .write = icu_write, + .read = icu_read, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 2, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 2, + }, +}; + +static void rxicu_realize(DeviceState *dev, Error **errp) +{ + RXICUState *icu = RX_ICU(dev); + int i, j; + + if (icu->init_sense == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "rx_icu: trigger-level property must be set."); + return; + } + for (i = j = 0; i < NR_IRQS; i++) { + if (icu->init_sense[j] == i) { + icu->src[i].sense = TRG_LEVEL; + if (j < icu->nr_sense) { + j++; + } + } else { + icu->src[i].sense = TRG_PEDGE; + } + } + icu->req_irq = -1; +} + +static void rxicu_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RXICUState *icu = RX_ICU(obj); + + memory_region_init_io(&icu->memory, OBJECT(icu), &icu_ops, + icu, "rx-icu", 0x600); + sysbus_init_mmio(d, &icu->memory); + + qdev_init_gpio_in(DEVICE(d), rxicu_set_irq, NR_IRQS); + qdev_init_gpio_in_named(DEVICE(d), rxicu_ack_irq, "ack", 1); + sysbus_init_irq(d, &icu->_irq); + sysbus_init_irq(d, &icu->_fir); + sysbus_init_irq(d, &icu->_swi); +} + +static void rxicu_fini(Object *obj) +{ + RXICUState *icu = RX_ICU(obj); + g_free(icu->map); + g_free(icu->init_sense); +} + +static const VMStateDescription vmstate_rxicu = { + .name = "rx-icu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(ir, RXICUState, NR_IRQS), + VMSTATE_UINT8_ARRAY(dtcer, RXICUState, NR_IRQS), + VMSTATE_UINT8_ARRAY(ier, RXICUState, NR_IRQS / 8), + VMSTATE_UINT8_ARRAY(ipr, RXICUState, 142), + VMSTATE_UINT8_ARRAY(dmasr, RXICUState, 4), + VMSTATE_UINT16(fir, RXICUState), + VMSTATE_UINT8(nmisr, RXICUState), + VMSTATE_UINT8(nmier, RXICUState), + VMSTATE_UINT8(nmiclr, RXICUState), + VMSTATE_UINT8(nmicr, RXICUState), + VMSTATE_INT16(req_irq, RXICUState), + VMSTATE_END_OF_LIST() + } +}; + +static Property rxicu_properties[] = { + DEFINE_PROP_ARRAY("ipr-map", RXICUState, nr_irqs, map, + qdev_prop_uint8, uint8_t), + DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense, + qdev_prop_uint8, uint8_t), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rxicu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rxicu_realize; + dc->vmsd = &vmstate_rxicu; + device_class_set_props(dc, rxicu_properties); +} + +static const TypeInfo rxicu_info = { + .name = TYPE_RX_ICU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RXICUState), + .instance_init = rxicu_init, + .instance_finalize = rxicu_fini, + .class_init = rxicu_class_init, +}; + +static void rxicu_register_types(void) +{ + type_register_static(&rxicu_info); +} + +type_init(rxicu_register_types) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 94d19571f7..28947ef3e0 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -4,6 +4,9 @@ config HTIF config HART bool +config IBEX + bool + config SIFIVE bool select MSI_NONBROKEN @@ -29,6 +32,7 @@ config SPIKE config OPENTITAN bool + select IBEX select HART select UNIMP diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index f6776da8e9..19223e4c29 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -25,12 +25,14 @@ #include "hw/misc/unimp.h" #include "hw/riscv/boot.h" #include "exec/address-spaces.h" +#include "qemu/units.h" +#include "sysemu/sysemu.h" static const struct MemmapEntry { hwaddr base; hwaddr size; } ibex_memmap[] = { - [IBEX_ROM] = { 0x00008000, 0xc000 }, + [IBEX_ROM] = { 0x00008000, 16 * KiB }, [IBEX_RAM] = { 0x10000000, 0x10000 }, [IBEX_FLASH] = { 0x20000000, 0x80000 }, [IBEX_UART] = { 0x40000000, 0x10000 }, @@ -51,7 +53,7 @@ static const struct MemmapEntry { [IBEX_PADCTRL] = { 0x40160000, 0x10000 } }; -static void riscv_opentitan_init(MachineState *machine) +static void opentitan_board_init(MachineState *machine) { const struct MemmapEntry *memmap = ibex_memmap; OpenTitanState *s = g_new0(OpenTitanState, 1); @@ -68,7 +70,6 @@ static void riscv_opentitan_init(MachineState *machine) memory_region_add_subregion(sys_mem, memmap[IBEX_RAM].base, main_mem); - if (machine->firmware) { riscv_load_firmware(machine->firmware, memmap[IBEX_RAM].base, NULL); } @@ -78,29 +79,34 @@ static void riscv_opentitan_init(MachineState *machine) } } -static void riscv_opentitan_machine_init(MachineClass *mc) +static void opentitan_machine_init(MachineClass *mc) { mc->desc = "RISC-V Board compatible with OpenTitan"; - mc->init = riscv_opentitan_init; + mc->init = opentitan_board_init; mc->max_cpus = 1; mc->default_cpu_type = TYPE_RISCV_CPU_IBEX; } -DEFINE_MACHINE("opentitan", riscv_opentitan_machine_init) +DEFINE_MACHINE("opentitan", opentitan_machine_init) -static void riscv_lowrisc_ibex_soc_init(Object *obj) +static void lowrisc_ibex_soc_init(Object *obj) { LowRISCIbexSoCState *s = RISCV_IBEX_SOC(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, "uart", &s->uart, TYPE_IBEX_UART); } -static void riscv_lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) +static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) { const struct MemmapEntry *memmap = ibex_memmap; MachineState *ms = MACHINE(qdev_get_machine()); LowRISCIbexSoCState *s = RISCV_IBEX_SOC(dev_soc); MemoryRegion *sys_mem = get_system_memory(); + Error *err = NULL; object_property_set_str(OBJECT(&s->cpus), ms->cpu_type, "cpu-type", &error_abort); @@ -120,8 +126,35 @@ static void riscv_lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memory_region_add_subregion(sys_mem, memmap[IBEX_FLASH].base, &s->flash_mem); - create_unimplemented_device("riscv.lowrisc.ibex.uart", - memmap[IBEX_UART].base, memmap[IBEX_UART].size); + /* PLIC */ + sysbus_realize(SYS_BUS_DEVICE(&s->plic), &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->plic), 0, memmap[IBEX_PLIC].base); + + /* UART */ + qdev_prop_set_chr(DEVICE(&(s->uart)), "chardev", serial_hd(0)); + sysbus_realize(SYS_BUS_DEVICE(&s->uart), &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart), 0, memmap[IBEX_UART].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), + 0, qdev_get_gpio_in(DEVICE(&s->plic), + IBEX_UART_TX_WATERMARK_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), + 1, qdev_get_gpio_in(DEVICE(&s->plic), + IBEX_UART_RX_WATERMARK_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), + 2, qdev_get_gpio_in(DEVICE(&s->plic), + IBEX_UART_TX_EMPTY_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), + 3, qdev_get_gpio_in(DEVICE(&s->plic), + IBEX_UART_RX_OVERFLOW_IRQ)); + create_unimplemented_device("riscv.lowrisc.ibex.gpio", memmap[IBEX_GPIO].base, memmap[IBEX_GPIO].size); create_unimplemented_device("riscv.lowrisc.ibex.spi", @@ -140,8 +173,6 @@ static void riscv_lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memmap[IBEX_AES].base, memmap[IBEX_AES].size); create_unimplemented_device("riscv.lowrisc.ibex.hmac", memmap[IBEX_HMAC].base, memmap[IBEX_HMAC].size); - create_unimplemented_device("riscv.lowrisc.ibex.plic", - memmap[IBEX_PLIC].base, memmap[IBEX_PLIC].size); create_unimplemented_device("riscv.lowrisc.ibex.pinmux", memmap[IBEX_PINMUX].base, memmap[IBEX_PINMUX].size); create_unimplemented_device("riscv.lowrisc.ibex.alert_handler", @@ -154,26 +185,26 @@ static void riscv_lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) memmap[IBEX_PADCTRL].base, memmap[IBEX_PADCTRL].size); } -static void riscv_lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data) +static void lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->realize = riscv_lowrisc_ibex_soc_realize; + dc->realize = lowrisc_ibex_soc_realize; /* Reason: Uses serial_hds in realize function, thus can't be used twice */ dc->user_creatable = false; } -static const TypeInfo riscv_lowrisc_ibex_soc_type_info = { +static const TypeInfo lowrisc_ibex_soc_type_info = { .name = TYPE_RISCV_IBEX_SOC, .parent = TYPE_DEVICE, .instance_size = sizeof(LowRISCIbexSoCState), - .instance_init = riscv_lowrisc_ibex_soc_init, - .class_init = riscv_lowrisc_ibex_soc_class_init, + .instance_init = lowrisc_ibex_soc_init, + .class_init = lowrisc_ibex_soc_class_init, }; -static void riscv_lowrisc_ibex_soc_register_types(void) +static void lowrisc_ibex_soc_register_types(void) { - type_register_static(&riscv_lowrisc_ibex_soc_type_info); + type_register_static(&lowrisc_ibex_soc_type_info); } -type_init(riscv_lowrisc_ibex_soc_register_types) +type_init(lowrisc_ibex_soc_register_types) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 1c17d02cf0..0cb66ac4e2 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -75,7 +75,7 @@ static const struct MemmapEntry { [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 } }; -static void riscv_sifive_e_init(MachineState *machine) +static void sifive_e_machine_init(MachineState *machine) { const struct MemmapEntry *memmap = sifive_e_memmap; @@ -95,10 +95,16 @@ static void riscv_sifive_e_init(MachineState *machine) memmap[SIFIVE_E_DTIM].base, main_mem); /* Mask ROM reset vector */ - uint32_t reset_vec[2] = { - 0x204002b7, /* 0x1000: lui t0,0x20400 */ - 0x00028067, /* 0x1004: jr t0 */ - }; + uint32_t reset_vec[4]; + + if (s->revb) { + reset_vec[1] = 0x200102b7; /* 0x1004: lui t0,0x20010 */ + } else { + reset_vec[1] = 0x204002b7; /* 0x1004: lui t0,0x20400 */ + } + reset_vec[2] = 0x00028067; /* 0x1008: jr t0 */ + + reset_vec[0] = reset_vec[3] = 0; /* copy in the reset vector in little_endian byte order */ for (i = 0; i < sizeof(reset_vec) >> 2; i++) { @@ -112,8 +118,30 @@ static void riscv_sifive_e_init(MachineState *machine) } } +static bool sifive_e_machine_get_revb(Object *obj, Error **errp) +{ + SiFiveEState *s = RISCV_E_MACHINE(obj); + + return s->revb; +} + +static void sifive_e_machine_set_revb(Object *obj, bool value, Error **errp) +{ + SiFiveEState *s = RISCV_E_MACHINE(obj); + + s->revb = value; +} + static void sifive_e_machine_instance_init(Object *obj) { + SiFiveEState *s = RISCV_E_MACHINE(obj); + + s->revb = false; + object_property_add_bool(obj, "revb", sifive_e_machine_get_revb, + sifive_e_machine_set_revb); + object_property_set_description(obj, "revb", + "Set on to tell QEMU that it should model " + "the revB HiFive1 board"); } static void sifive_e_machine_class_init(ObjectClass *oc, void *data) @@ -121,7 +149,7 @@ static void sifive_e_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "RISC-V Board compatible with SiFive E SDK"; - mc->init = riscv_sifive_e_init; + mc->init = sifive_e_machine_init; mc->max_cpus = 1; mc->default_cpu_type = SIFIVE_E_CPU; } @@ -141,7 +169,7 @@ static void sifive_e_machine_init_register_types(void) type_init(sifive_e_machine_init_register_types) -static void riscv_sifive_e_soc_init(Object *obj) +static void sifive_e_soc_init(Object *obj) { MachineState *ms = MACHINE(qdev_get_machine()); SiFiveESoCState *s = RISCV_E_SOC(obj); @@ -153,7 +181,7 @@ static void riscv_sifive_e_soc_init(Object *obj) TYPE_SIFIVE_GPIO); } -static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) +static void sifive_e_soc_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); const struct MemmapEntry *memmap = sifive_e_memmap; @@ -236,26 +264,26 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) &s->xip_mem); } -static void riscv_sifive_e_soc_class_init(ObjectClass *oc, void *data) +static void sifive_e_soc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->realize = riscv_sifive_e_soc_realize; + dc->realize = sifive_e_soc_realize; /* Reason: Uses serial_hds in realize function, thus can't be used twice */ dc->user_creatable = false; } -static const TypeInfo riscv_sifive_e_soc_type_info = { +static const TypeInfo sifive_e_soc_type_info = { .name = TYPE_RISCV_E_SOC, .parent = TYPE_DEVICE, .instance_size = sizeof(SiFiveESoCState), - .instance_init = riscv_sifive_e_soc_init, - .class_init = riscv_sifive_e_soc_class_init, + .instance_init = sifive_e_soc_init, + .class_init = sifive_e_soc_class_init, }; -static void riscv_sifive_e_soc_register_types(void) +static void sifive_e_soc_register_types(void) { - type_register_static(&riscv_sifive_e_soc_type_info); + type_register_static(&sifive_e_soc_type_info); } -type_init(riscv_sifive_e_soc_register_types) +type_init(sifive_e_soc_register_types) diff --git a/hw/riscv/sifive_gpio.c b/hw/riscv/sifive_gpio.c index 5c7c596e6b..aac6b44cac 100644 --- a/hw/riscv/sifive_gpio.c +++ b/hw/riscv/sifive_gpio.c @@ -1,5 +1,5 @@ /* - * sifive System-on-Chip general purpose input/output register definition + * SiFive System-on-Chip general purpose input/output register definition * * Copyright 2019 AdaCore * @@ -14,13 +14,13 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/irq.h" +#include "hw/qdev-properties.h" #include "hw/riscv/sifive_gpio.h" #include "migration/vmstate.h" #include "trace.h" static void update_output_irq(SIFIVEGPIOState *s) { - uint32_t pending; uint32_t pin; @@ -29,7 +29,7 @@ static void update_output_irq(SIFIVEGPIOState *s) pending |= s->rise_ip & s->rise_ie; pending |= s->fall_ip & s->fall_ie; - for (int i = 0; i < SIFIVE_GPIO_PINS; i++) { + for (int i = 0; i < s->ngpio; i++) { pin = 1 << i; qemu_set_irq(s->irq[i], (pending & pin) != 0); trace_sifive_gpio_update_output_irq(i, (pending & pin) != 0); @@ -42,7 +42,7 @@ static void update_state(SIFIVEGPIOState *s) bool prev_ival, in, in_mask, port, out_xor, pull, output_en, input_en, rise_ip, fall_ip, low_ip, high_ip, oval, actual_value, ival; - for (i = 0; i < SIFIVE_GPIO_PINS; i++) { + for (i = 0; i < s->ngpio; i++) { prev_ival = extract32(s->value, i, 1); in = extract32(s->in, i, 1); @@ -76,7 +76,9 @@ static void update_state(SIFIVEGPIOState *s) actual_value = pull; } - qemu_set_irq(s->output[i], actual_value); + if (output_en) { + qemu_set_irq(s->output[i], actual_value); + } /* Input value */ ival = input_en && actual_value; @@ -186,7 +188,7 @@ static uint64_t sifive_gpio_read(void *opaque, hwaddr offset, unsigned int size) } static void sifive_gpio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned int size) + uint64_t value, unsigned int size) { SIFIVEGPIOState *s = SIFIVE_GPIO(opaque); @@ -318,7 +320,6 @@ static void sifive_gpio_reset(DeviceState *dev) s->out_xor = 0; s->in = 0; s->in_mask = 0; - } static const VMStateDescription vmstate_sifive_gpio = { @@ -342,43 +343,49 @@ static const VMStateDescription vmstate_sifive_gpio = { VMSTATE_UINT32(iof_en, SIFIVEGPIOState), VMSTATE_UINT32(iof_sel, SIFIVEGPIOState), VMSTATE_UINT32(out_xor, SIFIVEGPIOState), - VMSTATE_UINT32(in, SIFIVEGPIOState), - VMSTATE_UINT32(in_mask, SIFIVEGPIOState), + VMSTATE_UINT32(in, SIFIVEGPIOState), + VMSTATE_UINT32(in_mask, SIFIVEGPIOState), VMSTATE_END_OF_LIST() } }; -static void sifive_gpio_init(Object *obj) +static Property sifive_gpio_properties[] = { + DEFINE_PROP_UINT32("ngpio", SIFIVEGPIOState, ngpio, SIFIVE_GPIO_PINS), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sifive_gpio_realize(DeviceState *dev, Error **errp) { - SIFIVEGPIOState *s = SIFIVE_GPIO(obj); + SIFIVEGPIOState *s = SIFIVE_GPIO(dev); - memory_region_init_io(&s->mmio, obj, &gpio_ops, s, + memory_region_init_io(&s->mmio, OBJECT(dev), &gpio_ops, s, TYPE_SIFIVE_GPIO, SIFIVE_GPIO_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); - for (int i = 0; i < SIFIVE_GPIO_PINS; i++) { - sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]); + for (int i = 0; i < s->ngpio; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); } - qdev_init_gpio_in(DEVICE(s), sifive_gpio_set, SIFIVE_GPIO_PINS); - qdev_init_gpio_out(DEVICE(s), s->output, SIFIVE_GPIO_PINS); + qdev_init_gpio_in(DEVICE(s), sifive_gpio_set, s->ngpio); + qdev_init_gpio_out(DEVICE(s), s->output, s->ngpio); } static void sifive_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + device_class_set_props(dc, sifive_gpio_properties); dc->vmsd = &vmstate_sifive_gpio; + dc->realize = sifive_gpio_realize; dc->reset = sifive_gpio_reset; - dc->desc = "sifive GPIO"; + dc->desc = "SiFive GPIO"; } static const TypeInfo sifive_gpio_info = { .name = TYPE_SIFIVE_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SIFIVEGPIOState), - .instance_init = sifive_gpio_init, .class_init = sifive_gpio_class_init }; diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index ea197ab64f..7d051e7c92 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -11,8 +11,9 @@ * 1) CLINT (Core Level Interruptor) * 2) PLIC (Platform Level Interrupt Controller) * 3) PRCI (Power, Reset, Clock, Interrupt) - * 4) OTP (One-Time Programmable) memory with stored serial number - * 5) GEM (Gigabit Ethernet Controller) and management block + * 4) GPIO (General Purpose Input/Output Controller) + * 5) OTP (One-Time Programmable) memory with stored serial number + * 6) GEM (Gigabit Ethernet Controller) and management block * * This board currently generates devicetree dynamically that indicates at least * two harts and up to five harts. @@ -36,6 +37,7 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "hw/boards.h" +#include "hw/irq.h" #include "hw/loader.h" #include "hw/sysbus.h" #include "hw/char/serial.h" @@ -52,6 +54,7 @@ #include "net/eth.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" +#include "sysemu/runstate.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" @@ -75,11 +78,13 @@ static const struct MemmapEntry { [SIFIVE_U_PRCI] = { 0x10000000, 0x1000 }, [SIFIVE_U_UART0] = { 0x10010000, 0x1000 }, [SIFIVE_U_UART1] = { 0x10011000, 0x1000 }, + [SIFIVE_U_GPIO] = { 0x10060000, 0x1000 }, [SIFIVE_U_OTP] = { 0x10070000, 0x1000 }, - [SIFIVE_U_FLASH0] = { 0x20000000, 0x10000000 }, - [SIFIVE_U_DRAM] = { 0x80000000, 0x0 }, [SIFIVE_U_GEM] = { 0x10090000, 0x2000 }, [SIFIVE_U_GEM_MGMT] = { 0x100a0000, 0x1000 }, + [SIFIVE_U_DMC] = { 0x100b0000, 0x10000 }, + [SIFIVE_U_FLASH0] = { 0x20000000, 0x10000000 }, + [SIFIVE_U_DRAM] = { 0x80000000, 0x0 }, }; #define OTP_SERIAL 1 @@ -94,7 +99,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, uint32_t *cells; char *nodename; char ethclk_names[] = "pclk\0hclk"; - uint32_t plic_phandle, prci_phandle, phandle = 1; + uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1; uint32_t hfclk_phandle, rtcclk_phandle, phy_phandle; fdt = s->fdt = create_device_tree(&s->fdt_size); @@ -207,6 +212,17 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(cells); g_free(nodename); + nodename = g_strdup_printf("/soc/otp@%lx", + (long)memmap[SIFIVE_U_OTP].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "fuse-count", SIFIVE_U_OTP_REG_SIZE); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_OTP].base, + 0x0, memmap[SIFIVE_U_OTP].size); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "sifive,fu540-c000-otp"); + g_free(nodename); + prci_phandle = phandle++; nodename = g_strdup_printf("/soc/clock-controller@%lx", (long)memmap[SIFIVE_U_PRCI].base); @@ -257,6 +273,36 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(cells); g_free(nodename); + gpio_phandle = phandle++; + nodename = g_strdup_printf("/soc/gpio@%lx", + (long)memmap[SIFIVE_U_GPIO].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", gpio_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + prci_phandle, PRCI_CLK_TLCLK); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 2); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, nodename, "#gpio-cells", 2); + qemu_fdt_setprop(fdt, nodename, "gpio-controller", NULL, 0); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_GPIO].base, + 0x0, memmap[SIFIVE_U_GPIO].size); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_GPIO_IRQ0, + SIFIVE_U_GPIO_IRQ1, SIFIVE_U_GPIO_IRQ2, SIFIVE_U_GPIO_IRQ3, + SIFIVE_U_GPIO_IRQ4, SIFIVE_U_GPIO_IRQ5, SIFIVE_U_GPIO_IRQ6, + SIFIVE_U_GPIO_IRQ7, SIFIVE_U_GPIO_IRQ8, SIFIVE_U_GPIO_IRQ9, + SIFIVE_U_GPIO_IRQ10, SIFIVE_U_GPIO_IRQ11, SIFIVE_U_GPIO_IRQ12, + SIFIVE_U_GPIO_IRQ13, SIFIVE_U_GPIO_IRQ14, SIFIVE_U_GPIO_IRQ15); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,gpio0"); + g_free(nodename); + + nodename = g_strdup_printf("/gpio-restart"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "gpios", gpio_phandle, 10, 1); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "gpio-restart"); + g_free(nodename); + phy_phandle = phandle++; nodename = g_strdup_printf("/soc/ethernet@%lx", (long)memmap[SIFIVE_U_GEM].base); @@ -317,6 +363,14 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(nodename); } +static void sifive_u_machine_reset(void *opaque, int n, int level) +{ + /* gpio pin active low triggers reset */ + if (!level) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } +} + static void sifive_u_machine_init(MachineState *machine) { const struct MemmapEntry *memmap = sifive_u_memmap; @@ -345,11 +399,41 @@ static void sifive_u_machine_init(MachineState *machine) memory_region_add_subregion(system_memory, memmap[SIFIVE_U_FLASH0].base, flash0); + /* register gpio-restart */ + qdev_connect_gpio_out(DEVICE(&(s->soc.gpio)), 10, + qemu_allocate_irq(sifive_u_machine_reset, NULL, 0)); + /* create device tree */ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); - riscv_find_and_load_firmware(machine, BIOS_FILENAME, - memmap[SIFIVE_U_DRAM].base, NULL); + if (s->start_in_flash) { + /* + * If start_in_flash property is given, assign s->msel to a value + * that representing booting from QSPI0 memory-mapped flash. + * + * This also means that when both start_in_flash and msel properties + * are given, start_in_flash takes the precedence over msel. + * + * Note this is to keep backward compatibility not to break existing + * users that use start_in_flash property. + */ + s->msel = MSEL_MEMMAP_QSPI0_FLASH; + } + + switch (s->msel) { + case MSEL_MEMMAP_QSPI0_FLASH: + start_addr = memmap[SIFIVE_U_FLASH0].base; + break; + case MSEL_L2LIM_QSPI0_FLASH: + case MSEL_L2LIM_QSPI2_SD: + start_addr = memmap[SIFIVE_U_L2LIM].base; + break; + default: + start_addr = memmap[SIFIVE_U_DRAM].base; + break; + } + + riscv_find_and_load_firmware(machine, BIOS_FILENAME, start_addr, NULL); if (machine->kernel_filename) { uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename, @@ -367,24 +451,20 @@ static void sifive_u_machine_init(MachineState *machine) } } - if (s->start_in_flash) { - start_addr = memmap[SIFIVE_U_FLASH0].base; - } - /* reset vector */ uint32_t reset_vec[8] = { + s->msel, /* MSEL pin state */ 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */ - 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */ + 0x01c28593, /* addi a1, t0, %pcrel_lo(1b) */ 0xf1402573, /* csrr a0, mhartid */ #if defined(TARGET_RISCV32) 0x0182a283, /* lw t0, 24(t0) */ #elif defined(TARGET_RISCV64) - 0x0182b283, /* ld t0, 24(t0) */ + 0x0182e283, /* lwu t0, 24(t0) */ #endif 0x00028067, /* jr t0 */ 0x00000000, start_addr, /* start: .dword */ - 0x00000000, /* dtb: */ }; @@ -421,14 +501,16 @@ static void sifive_u_machine_set_start_in_flash(Object *obj, bool value, Error * s->start_in_flash = value; } -static void sifive_u_machine_get_serial(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void sifive_u_machine_get_uint32_prop(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { visit_type_uint32(v, name, (uint32_t *)opaque, errp); } -static void sifive_u_machine_set_serial(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void sifive_u_machine_set_uint32_prop(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { visit_type_uint32(v, name, (uint32_t *)opaque, errp); } @@ -443,12 +525,20 @@ static void sifive_u_machine_instance_init(Object *obj) sifive_u_machine_set_start_in_flash); object_property_set_description(obj, "start-in-flash", "Set on to tell QEMU's ROM to jump to " - "flash. Otherwise QEMU will jump to DRAM"); + "flash. Otherwise QEMU will jump to DRAM " + "or L2LIM depending on the msel value"); + + s->msel = 0; + object_property_add(obj, "msel", "uint32", + sifive_u_machine_get_uint32_prop, + sifive_u_machine_set_uint32_prop, NULL, &s->msel); + object_property_set_description(obj, "msel", + "Mode Select (MSEL[3:0]) pin state"); s->serial = OTP_SERIAL; object_property_add(obj, "serial", "uint32", - sifive_u_machine_get_serial, - sifive_u_machine_set_serial, NULL, &s->serial); + sifive_u_machine_get_uint32_prop, + sifive_u_machine_set_uint32_prop, NULL, &s->serial); object_property_set_description(obj, "serial", "Board serial number"); } @@ -504,6 +594,7 @@ static void sifive_u_soc_instance_init(Object *obj) object_initialize_child(obj, "prci", &s->prci, TYPE_SIFIVE_U_PRCI); object_initialize_child(obj, "otp", &s->otp, TYPE_SIFIVE_U_OTP); object_initialize_child(obj, "gem", &s->gem, TYPE_CADENCE_GEM); + object_initialize_child(obj, "gpio", &s->gpio, TYPE_SIFIVE_GPIO); } static void sifive_u_soc_realize(DeviceState *dev, Error **errp) @@ -514,7 +605,6 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) MemoryRegion *system_memory = get_system_memory(); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1); - qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES]; char *plic_hart_config; size_t plic_hart_config_len; int i; @@ -590,14 +680,24 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(&s->prci), &err); sysbus_mmio_map(SYS_BUS_DEVICE(&s->prci), 0, memmap[SIFIVE_U_PRCI].base); + qdev_prop_set_uint32(DEVICE(&s->gpio), "ngpio", 16); + sysbus_realize(SYS_BUS_DEVICE(&s->gpio), &err); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, memmap[SIFIVE_U_GPIO].base); + + /* Pass all GPIOs to the SOC layer so they are available to the board */ + qdev_pass_gpios(DEVICE(&s->gpio), dev, NULL); + + /* Connect GPIO interrupts to the PLIC */ + for (i = 0; i < 16; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), i, + qdev_get_gpio_in(DEVICE(s->plic), + SIFIVE_U_GPIO_IRQ0 + i)); + } + qdev_prop_set_uint32(DEVICE(&s->otp), "serial", s->serial); sysbus_realize(SYS_BUS_DEVICE(&s->otp), &err); sysbus_mmio_map(SYS_BUS_DEVICE(&s->otp), 0, memmap[SIFIVE_U_OTP].base); - for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) { - plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i); - } - if (nd->used) { qemu_check_nic_model(nd, TYPE_CADENCE_GEM); qdev_set_nic_properties(DEVICE(&s->gem), nd); @@ -611,10 +711,13 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0, - plic_gpios[SIFIVE_U_GEM_IRQ]); + qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_GEM_IRQ)); create_unimplemented_device("riscv.sifive.u.gem-mgmt", memmap[SIFIVE_U_GEM_MGMT].base, memmap[SIFIVE_U_GEM_MGMT].size); + + create_unimplemented_device("riscv.sifive.u.dmc", + memmap[SIFIVE_U_DMC].base, memmap[SIFIVE_U_DMC].size); } static Property sifive_u_soc_props[] = { diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig new file mode 100644 index 0000000000..2b297c5a6a --- /dev/null +++ b/hw/rx/Kconfig @@ -0,0 +1,10 @@ +config RX62N_MCU + bool + select RX_ICU + select RENESAS_TMR + select RENESAS_CMT + select RENESAS_SCI + +config RX_GDBSIM + bool + select RX62N_MCU diff --git a/hw/rx/Makefile.objs b/hw/rx/Makefile.objs new file mode 100644 index 0000000000..4ef6b9e5b1 --- /dev/null +++ b/hw/rx/Makefile.objs @@ -0,0 +1,2 @@ +obj-$(CONFIG_RX62N_MCU) += rx62n.o +obj-$(CONFIG_RX_GDBSIM) += rx-gdbsim.o diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c new file mode 100644 index 0000000000..b8a56fa7af --- /dev/null +++ b/hw/rx/rx-gdbsim.c @@ -0,0 +1,198 @@ +/* + * RX QEMU GDB simulator + * + * Copyright (c) 2019 Yoshinori Sato + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/loader.h" +#include "hw/rx/rx62n.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "sysemu/device_tree.h" +#include "hw/boards.h" + +/* Same address of GDB integrated simulator */ +#define SDRAM_BASE EXT_CS_BASE + +typedef struct RxGdbSimMachineClass { + /*< private >*/ + MachineClass parent_class; + /*< public >*/ + const char *mcu_name; + uint32_t xtal_freq_hz; +} RxGdbSimMachineClass; + +typedef struct RxGdbSimMachineState { + /*< private >*/ + MachineState parent_obj; + /*< public >*/ + RX62NState mcu; +} RxGdbSimMachineState; + +#define TYPE_RX_GDBSIM_MACHINE MACHINE_TYPE_NAME("rx62n-common") + +#define RX_GDBSIM_MACHINE(obj) \ + OBJECT_CHECK(RxGdbSimMachineState, (obj), TYPE_RX_GDBSIM_MACHINE) + +#define RX_GDBSIM_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(RxGdbSimMachineClass, (klass), TYPE_RX_GDBSIM_MACHINE) +#define RX_GDBSIM_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RxGdbSimMachineClass, (obj), TYPE_RX_GDBSIM_MACHINE) + +static void rx_load_image(RXCPU *cpu, const char *filename, + uint32_t start, uint32_t size) +{ + static uint32_t extable[32]; + long kernel_size; + int i; + + kernel_size = load_image_targphys(filename, start, size); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", filename); + exit(1); + } + cpu->env.pc = start; + + /* setup exception trap trampoline */ + /* linux kernel only works little-endian mode */ + for (i = 0; i < ARRAY_SIZE(extable); i++) { + extable[i] = cpu_to_le32(0x10 + i * 4); + } + rom_add_blob_fixed("extable", extable, sizeof(extable), VECTOR_TABLE_BASE); +} + +static void rx_gdbsim_init(MachineState *machine) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + RxGdbSimMachineState *s = RX_GDBSIM_MACHINE(machine); + RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_GET_CLASS(machine); + MemoryRegion *sysmem = get_system_memory(); + const char *kernel_filename = machine->kernel_filename; + const char *dtb_filename = machine->dtb; + + if (machine->ram_size < mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be more than %s", sz); + g_free(sz); + } + + /* Allocate memory space */ + memory_region_add_subregion(sysmem, SDRAM_BASE, machine->ram); + + /* Initialize MCU */ + object_initialize_child(OBJECT(machine), "mcu", &s->mcu, rxc->mcu_name); + object_property_set_link(OBJECT(&s->mcu), OBJECT(sysmem), + "main-bus", &error_abort); + object_property_set_uint(OBJECT(&s->mcu), rxc->xtal_freq_hz, + "xtal-frequency-hz", &error_abort); + object_property_set_bool(OBJECT(&s->mcu), kernel_filename != NULL, + "load-kernel", &error_abort); + qdev_realize(DEVICE(&s->mcu), NULL, &error_abort); + + /* Load kernel and dtb */ + if (kernel_filename) { + ram_addr_t kernel_offset; + + /* + * The kernel image is loaded into + * the latter half of the SDRAM space. + */ + kernel_offset = machine->ram_size / 2; + rx_load_image(RXCPU(first_cpu), kernel_filename, + SDRAM_BASE + kernel_offset, kernel_offset); + if (dtb_filename) { + ram_addr_t dtb_offset; + int dtb_size; + void *dtb; + + dtb = load_device_tree(dtb_filename, &dtb_size); + if (dtb == NULL) { + error_report("Couldn't open dtb file %s", dtb_filename); + exit(1); + } + if (machine->kernel_cmdline && + qemu_fdt_setprop_string(dtb, "/chosen", "bootargs", + machine->kernel_cmdline) < 0) { + error_report("Couldn't set /chosen/bootargs"); + exit(1); + } + /* DTB is located at the end of SDRAM space. */ + dtb_offset = machine->ram_size - dtb_size; + rom_add_blob_fixed("dtb", dtb, dtb_size, + SDRAM_BASE + dtb_offset); + /* Set dtb address to R1 */ + RXCPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset; + } + } +} + +static void rx_gdbsim_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = rx_gdbsim_init; + mc->default_cpu_type = TYPE_RX62N_CPU; + mc->default_ram_size = 16 * MiB; + mc->default_ram_id = "ext-sdram"; +} + +static void rx62n7_class_init(ObjectClass *oc, void *data) +{ + RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + rxc->mcu_name = TYPE_R5F562N7_MCU; + rxc->xtal_freq_hz = 12 * 1000 * 1000; + mc->desc = "gdb simulator (R5F562N7 MCU and external RAM)"; +}; + +static void rx62n8_class_init(ObjectClass *oc, void *data) +{ + RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + rxc->mcu_name = TYPE_R5F562N8_MCU; + rxc->xtal_freq_hz = 12 * 1000 * 1000; + mc->desc = "gdb simulator (R5F562N8 MCU and external RAM)"; +}; + +static const TypeInfo rx_gdbsim_types[] = { + { + .name = MACHINE_TYPE_NAME("gdbsim-r5f562n7"), + .parent = TYPE_RX_GDBSIM_MACHINE, + .class_init = rx62n7_class_init, + }, { + .name = MACHINE_TYPE_NAME("gdbsim-r5f562n8"), + .parent = TYPE_RX_GDBSIM_MACHINE, + .class_init = rx62n8_class_init, + }, { + .name = TYPE_RX_GDBSIM_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(RxGdbSimMachineState), + .class_size = sizeof(RxGdbSimMachineClass), + .class_init = rx_gdbsim_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(rx_gdbsim_types) diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c new file mode 100644 index 0000000000..b9c217ebfa --- /dev/null +++ b/hw/rx/rx62n.c @@ -0,0 +1,323 @@ +/* + * RX62N Microcontroller + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * Copyright (c) 2020 Philippe Mathieu-Daudé + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/hw.h" +#include "hw/rx/rx62n.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/qdev-properties.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "cpu.h" + +/* + * RX62N Internal Memory + */ +#define RX62N_IRAM_BASE 0x00000000 +#define RX62N_DFLASH_BASE 0x00100000 +#define RX62N_CFLASH_BASE 0xfff80000 + +/* + * RX62N Peripheral Address + * See users manual section 5 + */ +#define RX62N_ICU_BASE 0x00087000 +#define RX62N_TMR_BASE 0x00088200 +#define RX62N_CMT_BASE 0x00088000 +#define RX62N_SCI_BASE 0x00088240 + +/* + * RX62N Peripheral IRQ + * See users manual section 11 + */ +#define RX62N_TMR_IRQ 174 +#define RX62N_CMT_IRQ 28 +#define RX62N_SCI_IRQ 214 + +#define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000) +#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000) +#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000) + +typedef struct RX62NClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + const char *name; + uint64_t ram_size; + uint64_t rom_flash_size; + uint64_t data_flash_size; +} RX62NClass; + +#define RX62N_MCU_CLASS(klass) \ + OBJECT_CLASS_CHECK(RX62NClass, (klass), TYPE_RX62N_MCU) +#define RX62N_MCU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RX62NClass, (obj), TYPE_RX62N_MCU) + +/* + * IRQ -> IPR mapping table + * 0x00 - 0x91: IPR no (IPR00 to IPR91) + * 0xff: IPR not assigned + * See "11.3.1 Interrupt Vector Table" in hardware manual. + */ +static const uint8_t ipr_table[NR_IRQS] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 15 */ + 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0x02, + 0xff, 0xff, 0xff, 0x03, 0x04, 0x05, 0x06, 0x07, /* 31 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x14, 0x14, 0x14, /* 47 */ + 0x15, 0x15, 0x15, 0x15, 0xff, 0xff, 0xff, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1d, 0x1e, 0x1f, /* 63 */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 79 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x3a, 0x3b, 0x3c, 0xff, 0xff, 0xff, /* 95 */ + 0x40, 0xff, 0x44, 0x45, 0xff, 0xff, 0x48, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 111 */ + 0xff, 0xff, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, + 0x52, 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, /* 127 */ + 0x56, 0x57, 0x57, 0x57, 0x57, 0x58, 0x59, 0x59, + 0x59, 0x59, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, /* 143 */ + 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, + 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x62, /* 159 */ + 0x62, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x66, + 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, /* 175 */ + 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, + 0x6b, 0x6b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 191 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0xff, 0xff, 0xff, 0xff, /* 207 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x80, + 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, /* 223 */ + 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0xff, 0xff, + 0xff, 0xff, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, /* 239 */ + 0x86, 0x86, 0xff, 0xff, 0xff, 0xff, 0x88, 0x89, + 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, /* 255 */ +}; + +/* + * Level triggerd IRQ list + * Not listed IRQ is Edge trigger. + * See "11.3.1 Interrupt Vector Table" in hardware manual. + */ +static const uint8_t levelirq[] = { + 16, 21, 32, 44, 47, 48, 51, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 90, 91, 170, 171, 172, 173, 214, + 217, 218, 221, 222, 225, 226, 229, 234, 237, 238, + 241, 246, 249, 250, 253, +}; + +static void register_icu(RX62NState *s) +{ + int i; + SysBusDevice *icu; + + object_initialize_child(OBJECT(s), "icu", &s->icu, TYPE_RX_ICU); + icu = SYS_BUS_DEVICE(&s->icu); + qdev_prop_set_uint32(DEVICE(icu), "len-ipr-map", NR_IRQS); + for (i = 0; i < NR_IRQS; i++) { + char propname[32]; + snprintf(propname, sizeof(propname), "ipr-map[%d]", i); + qdev_prop_set_uint32(DEVICE(icu), propname, ipr_table[i]); + } + qdev_prop_set_uint32(DEVICE(icu), "len-trigger-level", + ARRAY_SIZE(levelirq)); + for (i = 0; i < ARRAY_SIZE(levelirq); i++) { + char propname[32]; + snprintf(propname, sizeof(propname), "trigger-level[%d]", i); + qdev_prop_set_uint32(DEVICE(icu), propname, levelirq[i]); + } + + for (i = 0; i < NR_IRQS; i++) { + s->irq[i] = qdev_get_gpio_in(DEVICE(icu), i); + } + sysbus_realize(icu, &error_abort); + sysbus_connect_irq(icu, 0, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_IRQ)); + sysbus_connect_irq(icu, 1, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_FIR)); + sysbus_connect_irq(icu, 2, s->irq[SWI]); + sysbus_mmio_map(SYS_BUS_DEVICE(icu), 0, RX62N_ICU_BASE); +} + +static void register_tmr(RX62NState *s, int unit) +{ + SysBusDevice *tmr; + int i, irqbase; + + object_initialize_child(OBJECT(s), "tmr[*]", + &s->tmr[unit], TYPE_RENESAS_TMR); + tmr = SYS_BUS_DEVICE(&s->tmr[unit]); + qdev_prop_set_uint64(DEVICE(tmr), "input-freq", s->pclk_freq_hz); + sysbus_realize(tmr, &error_abort); + + irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit; + for (i = 0; i < TMR_NR_IRQ; i++) { + sysbus_connect_irq(tmr, i, s->irq[irqbase + i]); + } + sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10); +} + +static void register_cmt(RX62NState *s, int unit) +{ + SysBusDevice *cmt; + int i, irqbase; + + object_initialize_child(OBJECT(s), "cmt[*]", + &s->cmt[unit], TYPE_RENESAS_CMT); + cmt = SYS_BUS_DEVICE(&s->cmt[unit]); + qdev_prop_set_uint64(DEVICE(cmt), "input-freq", s->pclk_freq_hz); + sysbus_realize(cmt, &error_abort); + + irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit; + for (i = 0; i < CMT_NR_IRQ; i++) { + sysbus_connect_irq(cmt, i, s->irq[irqbase + i]); + } + sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10); +} + +static void register_sci(RX62NState *s, int unit) +{ + SysBusDevice *sci; + int i, irqbase; + + object_initialize_child(OBJECT(s), "sci[*]", + &s->sci[unit], TYPE_RENESAS_SCI); + sci = SYS_BUS_DEVICE(&s->sci[unit]); + qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit)); + qdev_prop_set_uint64(DEVICE(sci), "input-freq", s->pclk_freq_hz); + sysbus_realize(sci, &error_abort); + + irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit; + for (i = 0; i < SCI_NR_IRQ; i++) { + sysbus_connect_irq(sci, i, s->irq[irqbase + i]); + } + sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08); +} + +static void rx62n_realize(DeviceState *dev, Error **errp) +{ + RX62NState *s = RX62N_MCU(dev); + RX62NClass *rxc = RX62N_MCU_GET_CLASS(dev); + + if (s->xtal_freq_hz == 0) { + error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); + return; + } + /* XTAL range: 8-14 MHz */ + if (s->xtal_freq_hz < RX62N_XTAL_MIN_HZ + || s->xtal_freq_hz > RX62N_XTAL_MAX_HZ) { + error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range."); + return; + } + /* Use a 4x fixed multiplier */ + s->pclk_freq_hz = 4 * s->xtal_freq_hz; + /* PCLK range: 8-50 MHz */ + assert(s->pclk_freq_hz <= RX62N_PCLK_MAX_HZ); + + memory_region_init_ram(&s->iram, OBJECT(dev), "iram", + rxc->ram_size, &error_abort); + memory_region_add_subregion(s->sysmem, RX62N_IRAM_BASE, &s->iram); + memory_region_init_rom(&s->d_flash, OBJECT(dev), "flash-data", + rxc->data_flash_size, &error_abort); + memory_region_add_subregion(s->sysmem, RX62N_DFLASH_BASE, &s->d_flash); + memory_region_init_rom(&s->c_flash, OBJECT(dev), "flash-code", + rxc->rom_flash_size, &error_abort); + memory_region_add_subregion(s->sysmem, RX62N_CFLASH_BASE, &s->c_flash); + + if (!s->kernel) { + if (bios_name) { + rom_add_file_fixed(bios_name, RX62N_CFLASH_BASE, 0); + } else if (!qtest_enabled()) { + error_report("No bios or kernel specified"); + exit(1); + } + } + + /* Initialize CPU */ + object_initialize_child(OBJECT(s), "cpu", &s->cpu, TYPE_RX62N_CPU); + qdev_realize(DEVICE(&s->cpu), NULL, &error_abort); + + register_icu(s); + s->cpu.env.ack = qdev_get_gpio_in_named(DEVICE(&s->icu), "ack", 0); + register_tmr(s, 0); + register_tmr(s, 1); + register_cmt(s, 0); + register_cmt(s, 1); + register_sci(s, 0); +} + +static Property rx62n_properties[] = { + DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false), + DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rx62n_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rx62n_realize; + device_class_set_props(dc, rx62n_properties); +} + +static void r5f562n7_class_init(ObjectClass *oc, void *data) +{ + RX62NClass *rxc = RX62N_MCU_CLASS(oc); + + rxc->ram_size = 64 * KiB; + rxc->rom_flash_size = 384 * KiB; + rxc->data_flash_size = 32 * KiB; +}; + +static void r5f562n8_class_init(ObjectClass *oc, void *data) +{ + RX62NClass *rxc = RX62N_MCU_CLASS(oc); + + rxc->ram_size = 96 * KiB; + rxc->rom_flash_size = 512 * KiB; + rxc->data_flash_size = 32 * KiB; +}; + +static const TypeInfo rx62n_types[] = { + { + .name = TYPE_R5F562N7_MCU, + .parent = TYPE_RX62N_MCU, + .class_init = r5f562n7_class_init, + }, { + .name = TYPE_R5F562N8_MCU, + .parent = TYPE_RX62N_MCU, + .class_init = r5f562n8_class_init, + }, { + .name = TYPE_RX62N_MCU, + .parent = TYPE_DEVICE, + .instance_size = sizeof(RX62NState), + .class_size = sizeof(RX62NClass), + .class_init = rx62n_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(rx62n_types) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 5d8e08667e..d1e365e3e6 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -1335,11 +1335,20 @@ static void copy_schib_to_guest(SCHIB *dest, const SCHIB *src) } } -int css_do_stsch(SubchDev *sch, SCHIB *schib) +IOInstEnding css_do_stsch(SubchDev *sch, SCHIB *schib) { + int ret; + + /* + * For some subchannels, we may want to update parts of + * the schib (e.g., update path masks from the host device + * for passthrough subchannels). + */ + ret = s390_ccw_store(sch); + /* Use current status. */ copy_schib_to_guest(schib, &sch->curr_status); - return 0; + return ret; } static void copy_pmcw_from_guest(PMCW *dest, const PMCW *src) @@ -2161,30 +2170,23 @@ void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, } } -void css_queue_crw(uint8_t rsc, uint8_t erc, int solicited, - int chain, uint16_t rsid) +void css_crw_add_to_queue(CRW crw) { CrwContainer *crw_cont; - trace_css_crw(rsc, erc, rsid, chain ? "(chained)" : ""); + trace_css_crw((crw.flags & CRW_FLAGS_MASK_RSC) >> 8, + crw.flags & CRW_FLAGS_MASK_ERC, + crw.rsid, + (crw.flags & CRW_FLAGS_MASK_C) ? "(chained)" : ""); + /* TODO: Maybe use a static crw pool? */ crw_cont = g_try_new0(CrwContainer, 1); if (!crw_cont) { channel_subsys.crws_lost = true; return; } - crw_cont->crw.flags = (rsc << 8) | erc; - if (solicited) { - crw_cont->crw.flags |= CRW_FLAGS_MASK_S; - } - if (chain) { - crw_cont->crw.flags |= CRW_FLAGS_MASK_C; - } - crw_cont->crw.rsid = rsid; - if (channel_subsys.crws_lost) { - crw_cont->crw.flags |= CRW_FLAGS_MASK_R; - channel_subsys.crws_lost = false; - } + + crw_cont->crw = crw; QTAILQ_INSERT_TAIL(&channel_subsys.pending_crws, crw_cont, sibling); @@ -2195,6 +2197,27 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int solicited, } } +void css_queue_crw(uint8_t rsc, uint8_t erc, int solicited, + int chain, uint16_t rsid) +{ + CRW crw; + + crw.flags = (rsc << 8) | erc; + if (solicited) { + crw.flags |= CRW_FLAGS_MASK_S; + } + if (chain) { + crw.flags |= CRW_FLAGS_MASK_C; + } + crw.rsid = rsid; + if (channel_subsys.crws_lost) { + crw.flags |= CRW_FLAGS_MASK_R; + channel_subsys.crws_lost = false; + } + + css_crw_add_to_queue(crw); +} + void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, int hotplugged, int add) { diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index c48510f9e5..b497571863 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -51,6 +51,27 @@ int s390_ccw_clear(SubchDev *sch) return cdc->handle_clear(sch); } +IOInstEnding s390_ccw_store(SubchDev *sch) +{ + S390CCWDeviceClass *cdc = NULL; + int ret = IOINST_CC_EXPECTED; + + /* + * This code is called for both virtual and passthrough devices, + * but only applies to to the latter. This ugly check makes that + * distinction for us. + */ + if (object_dynamic_cast(OBJECT(sch->driver_data), TYPE_S390_CCW)) { + cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data); + } + + if (cdc && cdc->handle_store) { + ret = cdc->handle_store(sch); + } + + return ret; +} + static void s390_ccw_get_dev_info(S390CCWDevice *cdev, char *sysfsdev, Error **errp) diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index d660714443..f8ac3ec6e3 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -30,6 +30,7 @@ #include "sh7750_regs.h" #include "sh7750_regnames.h" #include "hw/sh4/sh_intc.h" +#include "hw/timer/tmu012.h" #include "cpu.h" #include "exec/exec-all.h" diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 59b3f44d69..59a667c503 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -35,3 +35,9 @@ config CMSDK_APB_TIMER config CMSDK_APB_DUALTIMER bool select PTIMER + +config RENESAS_TMR + bool + +config RENESAS_CMT + bool diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index dece235fd7..a39f6ec0c2 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -23,6 +23,8 @@ common-obj-$(CONFIG_OMAP) += omap_gptimer.o common-obj-$(CONFIG_OMAP) += omap_synctimer.o common-obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o common-obj-$(CONFIG_SH4) += sh_timer.o +common-obj-$(CONFIG_RENESAS_TMR) += renesas_tmr.o +common-obj-$(CONFIG_RENESAS_CMT) += renesas_cmt.o common-obj-$(CONFIG_DIGIC) += digic-timer.o common-obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c new file mode 100644 index 0000000000..2e0fd21a36 --- /dev/null +++ b/hw/timer/renesas_cmt.c @@ -0,0 +1,283 @@ +/* + * Renesas 16bit Compare-match timer + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/timer/renesas_cmt.h" +#include "migration/vmstate.h" + +/* + * +0 CMSTR - common control + * +2 CMCR - ch0 + * +4 CMCNT - ch0 + * +6 CMCOR - ch0 + * +8 CMCR - ch1 + * +10 CMCNT - ch1 + * +12 CMCOR - ch1 + * If we think that the address of CH 0 has an offset of +2, + * we can treat it with the same address as CH 1, so define it like that. + */ +REG16(CMSTR, 0) + FIELD(CMSTR, STR0, 0, 1) + FIELD(CMSTR, STR1, 1, 1) + FIELD(CMSTR, STR, 0, 2) +/* This addeess is channel offset */ +REG16(CMCR, 0) + FIELD(CMCR, CKS, 0, 2) + FIELD(CMCR, CMIE, 6, 1) +REG16(CMCNT, 2) +REG16(CMCOR, 4) + +static void update_events(RCMTState *cmt, int ch) +{ + int64_t next_time; + + if ((cmt->cmstr & (1 << ch)) == 0) { + /* count disable, so not happened next event. */ + return ; + } + next_time = cmt->cmcor[ch] - cmt->cmcnt[ch]; + next_time *= NANOSECONDS_PER_SECOND; + next_time /= cmt->input_freq; + /* + * CKS -> div rate + * 0 -> 8 (1 << 3) + * 1 -> 32 (1 << 5) + * 2 -> 128 (1 << 7) + * 3 -> 512 (1 << 9) + */ + next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2); + next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod(&cmt->timer[ch], next_time); +} + +static int64_t read_cmcnt(RCMTState *cmt, int ch) +{ + int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + if (cmt->cmstr & (1 << ch)) { + delta = (now - cmt->tick[ch]); + delta /= NANOSECONDS_PER_SECOND; + delta /= cmt->input_freq; + delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2); + cmt->tick[ch] = now; + return cmt->cmcnt[ch] + delta; + } else { + return cmt->cmcnt[ch]; + } +} + +static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size) +{ + RCMTState *cmt = opaque; + int ch = offset / 0x08; + uint64_t ret; + + if (offset == A_CMSTR) { + ret = 0; + ret = FIELD_DP16(ret, CMSTR, STR, + FIELD_EX16(cmt->cmstr, CMSTR, STR)); + return ret; + } else { + offset &= 0x07; + if (ch == 0) { + offset -= 0x02; + } + switch (offset) { + case A_CMCR: + ret = 0; + ret = FIELD_DP16(ret, CMCR, CKS, + FIELD_EX16(cmt->cmstr, CMCR, CKS)); + ret = FIELD_DP16(ret, CMCR, CMIE, + FIELD_EX16(cmt->cmstr, CMCR, CMIE)); + return ret; + case A_CMCNT: + return read_cmcnt(cmt, ch); + case A_CMCOR: + return cmt->cmcor[ch]; + } + } + qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " " + "not implemented\n", + offset); + return UINT64_MAX; +} + +static void start_stop(RCMTState *cmt, int ch, int st) +{ + if (st) { + update_events(cmt, ch); + } else { + timer_del(&cmt->timer[ch]); + } +} + +static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + RCMTState *cmt = opaque; + int ch = offset / 0x08; + + if (offset == A_CMSTR) { + cmt->cmstr = FIELD_EX16(val, CMSTR, STR); + start_stop(cmt, 0, FIELD_EX16(cmt->cmstr, CMSTR, STR0)); + start_stop(cmt, 1, FIELD_EX16(cmt->cmstr, CMSTR, STR1)); + } else { + offset &= 0x07; + if (ch == 0) { + offset -= 0x02; + } + switch (offset) { + case A_CMCR: + cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CKS, + FIELD_EX16(val, CMCR, CKS)); + cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE, + FIELD_EX16(val, CMCR, CMIE)); + break; + case 2: + cmt->cmcnt[ch] = val; + break; + case 4: + cmt->cmcor[ch] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " " + "not implemented\n", + offset); + return; + } + if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) { + update_events(cmt, ch); + } + } +} + +static const MemoryRegionOps cmt_ops = { + .write = cmt_write, + .read = cmt_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 2, + }, + .valid = { + .min_access_size = 2, + .max_access_size = 2, + }, +}; + +static void timer_events(RCMTState *cmt, int ch) +{ + cmt->cmcnt[ch] = 0; + cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + update_events(cmt, ch); + if (FIELD_EX16(cmt->cmcr[ch], CMCR, CMIE)) { + qemu_irq_pulse(cmt->cmi[ch]); + } +} + +static void timer_event0(void *opaque) +{ + RCMTState *cmt = opaque; + + timer_events(cmt, 0); +} + +static void timer_event1(void *opaque) +{ + RCMTState *cmt = opaque; + + timer_events(cmt, 1); +} + +static void rcmt_reset(DeviceState *dev) +{ + RCMTState *cmt = RCMT(dev); + cmt->cmstr = 0; + cmt->cmcr[0] = cmt->cmcr[1] = 0; + cmt->cmcnt[0] = cmt->cmcnt[1] = 0; + cmt->cmcor[0] = cmt->cmcor[1] = 0xffff; +} + +static void rcmt_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RCMTState *cmt = RCMT(obj); + int i; + + memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops, + cmt, "renesas-cmt", 0x10); + sysbus_init_mmio(d, &cmt->memory); + + for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) { + sysbus_init_irq(d, &cmt->cmi[i]); + } + timer_init_ns(&cmt->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, cmt); + timer_init_ns(&cmt->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, cmt); +} + +static const VMStateDescription vmstate_rcmt = { + .name = "rx-cmt", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(cmstr, RCMTState), + VMSTATE_UINT16_ARRAY(cmcr, RCMTState, CMT_CH), + VMSTATE_UINT16_ARRAY(cmcnt, RCMTState, CMT_CH), + VMSTATE_UINT16_ARRAY(cmcor, RCMTState, CMT_CH), + VMSTATE_INT64_ARRAY(tick, RCMTState, CMT_CH), + VMSTATE_TIMER_ARRAY(timer, RCMTState, CMT_CH), + VMSTATE_END_OF_LIST() + } +}; + +static Property rcmt_properties[] = { + DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rcmt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_rcmt; + dc->reset = rcmt_reset; + device_class_set_props(dc, rcmt_properties); +} + +static const TypeInfo rcmt_info = { + .name = TYPE_RENESAS_CMT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RCMTState), + .instance_init = rcmt_init, + .class_init = rcmt_class_init, +}; + +static void rcmt_register_types(void) +{ + type_register_static(&rcmt_info); +} + +type_init(rcmt_register_types) diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c new file mode 100644 index 0000000000..446f2eacdd --- /dev/null +++ b/hw/timer/renesas_tmr.c @@ -0,0 +1,477 @@ +/* + * Renesas 8bit timer + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/timer/renesas_tmr.h" +#include "migration/vmstate.h" + +REG8(TCR, 0) + FIELD(TCR, CCLR, 3, 2) + FIELD(TCR, OVIE, 5, 1) + FIELD(TCR, CMIEA, 6, 1) + FIELD(TCR, CMIEB, 7, 1) +REG8(TCSR, 2) + FIELD(TCSR, OSA, 0, 2) + FIELD(TCSR, OSB, 2, 2) + FIELD(TCSR, ADTE, 4, 2) +REG8(TCORA, 4) +REG8(TCORB, 6) +REG8(TCNT, 8) +REG8(TCCR, 10) + FIELD(TCCR, CKS, 0, 3) + FIELD(TCCR, CSS, 3, 2) + FIELD(TCCR, TMRIS, 7, 1) + +#define INTERNAL 0x01 +#define CASCADING 0x03 +#define CCLR_A 0x01 +#define CCLR_B 0x02 + +static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192}; + +static uint8_t concat_reg(uint8_t *reg) +{ + return (reg[0] << 8) | reg[1]; +} + +static void update_events(RTMRState *tmr, int ch) +{ + uint16_t diff[TMR_NR_EVENTS], min; + int64_t next_time; + int i, event; + + if (tmr->tccr[ch] == 0) { + return ; + } + if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) { + /* external clock mode */ + /* event not happened */ + return ; + } + if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CASCADING) { + /* cascading mode */ + if (ch == 1) { + tmr->next[ch] = none; + return ; + } + diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt); + diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt); + diff[ovi] = 0x10000 - concat_reg(tmr->tcnt); + } else { + /* separate mode */ + diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch]; + diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch]; + diff[ovi] = 0x100 - tmr->tcnt[ch]; + } + /* Search for the most recently occurring event. */ + for (event = 0, min = diff[0], i = 1; i < none; i++) { + if (min > diff[i]) { + event = i; + min = diff[i]; + } + } + tmr->next[ch] = event; + next_time = diff[event]; + next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)]; + next_time *= NANOSECONDS_PER_SECOND; + next_time /= tmr->input_freq; + next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod(&tmr->timer[ch], next_time); +} + +static int elapsed_time(RTMRState *tmr, int ch, int64_t delta) +{ + int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)]; + int et; + + tmr->div_round[ch] += delta; + if (divrate > 0) { + et = tmr->div_round[ch] / divrate; + tmr->div_round[ch] %= divrate; + } else { + /* disble clock. so no update */ + et = 0; + } + return et; +} + +static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch) +{ + int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int elapsed, ovf = 0; + uint16_t tcnt[2]; + uint32_t ret; + + delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq; + if (delta > 0) { + tmr->tick = now; + + if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) { + /* timer1 count update */ + elapsed = elapsed_time(tmr, 1, delta); + if (elapsed >= 0x100) { + ovf = elapsed >> 8; + } + tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff); + } + switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) { + case INTERNAL: + elapsed = elapsed_time(tmr, 0, delta); + tcnt[0] = tmr->tcnt[0] + elapsed; + break; + case CASCADING: + if (ovf > 0) { + tcnt[0] = tmr->tcnt[0] + ovf; + } + break; + } + } else { + tcnt[0] = tmr->tcnt[0]; + tcnt[1] = tmr->tcnt[1]; + } + if (size == 1) { + return tcnt[ch]; + } else { + ret = 0; + ret = deposit32(ret, 0, 8, tcnt[1]); + ret = deposit32(ret, 8, 8, tcnt[0]); + return ret; + } +} + +static uint8_t read_tccr(uint8_t r) +{ + uint8_t tccr = 0; + tccr = FIELD_DP8(tccr, TCCR, TMRIS, + FIELD_EX8(r, TCCR, TMRIS)); + tccr = FIELD_DP8(tccr, TCCR, CSS, + FIELD_EX8(r, TCCR, CSS)); + tccr = FIELD_DP8(tccr, TCCR, CKS, + FIELD_EX8(r, TCCR, CKS)); + return tccr; +} + +static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size) +{ + RTMRState *tmr = opaque; + int ch = addr & 1; + uint64_t ret; + + if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) { + qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%" + HWADDR_PRIX "\n", + addr); + return UINT64_MAX; + } + switch (addr & 0x0e) { + case A_TCR: + ret = 0; + ret = FIELD_DP8(ret, TCR, CCLR, + FIELD_EX8(tmr->tcr[ch], TCR, CCLR)); + ret = FIELD_DP8(ret, TCR, OVIE, + FIELD_EX8(tmr->tcr[ch], TCR, OVIE)); + ret = FIELD_DP8(ret, TCR, CMIEA, + FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)); + ret = FIELD_DP8(ret, TCR, CMIEB, + FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)); + return ret; + case A_TCSR: + ret = 0; + ret = FIELD_DP8(ret, TCSR, OSA, + FIELD_EX8(tmr->tcsr[ch], TCSR, OSA)); + ret = FIELD_DP8(ret, TCSR, OSB, + FIELD_EX8(tmr->tcsr[ch], TCSR, OSB)); + switch (ch) { + case 0: + ret = FIELD_DP8(ret, TCSR, ADTE, + FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE)); + break; + case 1: /* CH1 ADTE unimplement always 1 */ + ret = FIELD_DP8(ret, TCSR, ADTE, 1); + break; + } + return ret; + case A_TCORA: + if (size == 1) { + return tmr->tcora[ch]; + } else if (ch == 0) { + return concat_reg(tmr->tcora); + } + case A_TCORB: + if (size == 1) { + return tmr->tcorb[ch]; + } else { + return concat_reg(tmr->tcorb); + } + case A_TCNT: + return read_tcnt(tmr, size, ch); + case A_TCCR: + if (size == 1) { + return read_tccr(tmr->tccr[ch]); + } else { + return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]); + } + default: + qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX + " not implemented\n", + addr); + break; + } + return UINT64_MAX; +} + +static void tmr_write_count(RTMRState *tmr, int ch, unsigned size, + uint8_t *reg, uint64_t val) +{ + if (size == 1) { + reg[ch] = val; + update_events(tmr, ch); + } else { + reg[0] = extract32(val, 8, 8); + reg[1] = extract32(val, 0, 8); + update_events(tmr, 0); + update_events(tmr, 1); + } +} + +static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + RTMRState *tmr = opaque; + int ch = addr & 1; + + if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) { + qemu_log_mask(LOG_GUEST_ERROR, + "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n", + addr); + return; + } + switch (addr & 0x0e) { + case A_TCR: + tmr->tcr[ch] = val; + break; + case A_TCSR: + tmr->tcsr[ch] = val; + break; + case A_TCORA: + tmr_write_count(tmr, ch, size, tmr->tcora, val); + break; + case A_TCORB: + tmr_write_count(tmr, ch, size, tmr->tcorb, val); + break; + case A_TCNT: + tmr_write_count(tmr, ch, size, tmr->tcnt, val); + break; + case A_TCCR: + tmr_write_count(tmr, ch, size, tmr->tccr, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX + " not implemented\n", + addr); + break; + } +} + +static const MemoryRegionOps tmr_ops = { + .write = tmr_write, + .read = tmr_read, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 2, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 2, + }, +}; + +static void timer_events(RTMRState *tmr, int ch); + +static uint16_t issue_event(RTMRState *tmr, int ch, int sz, + uint16_t tcnt, uint16_t tcora, uint16_t tcorb) +{ + uint16_t ret = tcnt; + + switch (tmr->next[ch]) { + case none: + break; + case cmia: + if (tcnt >= tcora) { + if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) { + ret = tcnt - tcora; + } + if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) { + qemu_irq_pulse(tmr->cmia[ch]); + } + if (sz == 8 && ch == 0 && + FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CASCADING) { + tmr->tcnt[1]++; + timer_events(tmr, 1); + } + } + break; + case cmib: + if (tcnt >= tcorb) { + if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) { + ret = tcnt - tcorb; + } + if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) { + qemu_irq_pulse(tmr->cmib[ch]); + } + } + break; + case ovi: + if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) { + qemu_irq_pulse(tmr->ovi[ch]); + } + break; + default: + g_assert_not_reached(); + } + return ret; +} + +static void timer_events(RTMRState *tmr, int ch) +{ + uint16_t tcnt; + + tmr->tcnt[ch] = read_tcnt(tmr, 1, ch); + if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CASCADING) { + tmr->tcnt[ch] = issue_event(tmr, ch, 8, + tmr->tcnt[ch], + tmr->tcora[ch], + tmr->tcorb[ch]) & 0xff; + } else { + if (ch == 1) { + return ; + } + tcnt = issue_event(tmr, ch, 16, + concat_reg(tmr->tcnt), + concat_reg(tmr->tcora), + concat_reg(tmr->tcorb)); + tmr->tcnt[0] = (tcnt >> 8) & 0xff; + tmr->tcnt[1] = tcnt & 0xff; + } + update_events(tmr, ch); +} + +static void timer_event0(void *opaque) +{ + RTMRState *tmr = opaque; + + timer_events(tmr, 0); +} + +static void timer_event1(void *opaque) +{ + RTMRState *tmr = opaque; + + timer_events(tmr, 1); +} + +static void rtmr_reset(DeviceState *dev) +{ + RTMRState *tmr = RTMR(dev); + tmr->tcr[0] = tmr->tcr[1] = 0x00; + tmr->tcsr[0] = 0x00; + tmr->tcsr[1] = 0x10; + tmr->tcnt[0] = tmr->tcnt[1] = 0x00; + tmr->tcora[0] = tmr->tcora[1] = 0xff; + tmr->tcorb[0] = tmr->tcorb[1] = 0xff; + tmr->tccr[0] = tmr->tccr[1] = 0x00; + tmr->next[0] = tmr->next[1] = none; + tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void rtmr_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + RTMRState *tmr = RTMR(obj); + int i; + + memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops, + tmr, "renesas-tmr", 0x10); + sysbus_init_mmio(d, &tmr->memory); + + for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) { + sysbus_init_irq(d, &tmr->cmia[i]); + sysbus_init_irq(d, &tmr->cmib[i]); + sysbus_init_irq(d, &tmr->ovi[i]); + } + timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr); + timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr); +} + +static const VMStateDescription vmstate_rtmr = { + .name = "rx-tmr", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT64(tick, RTMRState), + VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH), + VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH), + VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH), + VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH), + VMSTATE_END_OF_LIST() + } +}; + +static Property rtmr_properties[] = { + DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void rtmr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_rtmr; + dc->reset = rtmr_reset; + device_class_set_props(dc, rtmr_properties); +} + +static const TypeInfo rtmr_info = { + .name = TYPE_RENESAS_TMR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RTMRState), + .instance_init = rtmr_init, + .class_init = rtmr_class_init, +}; + +static void rtmr_register_types(void) +{ + type_register_static(&rtmr_info); +} + +type_init(rtmr_register_types) diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 13c4051808..bb0e1c8ee5 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -9,10 +9,11 @@ */ #include "qemu/osdep.h" +#include "exec/memory.h" #include "hw/hw.h" #include "hw/irq.h" #include "hw/sh4/sh.h" -#include "qemu/timer.h" +#include "hw/timer/tmu012.h" #include "hw/ptimer.h" //#define DEBUG_TIMER diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 63406184d2..06e69d7066 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -41,7 +41,14 @@ struct VFIOCCWDevice { uint64_t async_cmd_region_size; uint64_t async_cmd_region_offset; struct ccw_cmd_region *async_cmd_region; + uint64_t schib_region_size; + uint64_t schib_region_offset; + struct ccw_schib_region *schib_region; + uint64_t crw_region_size; + uint64_t crw_region_offset; + struct ccw_crw_region *crw_region; EventNotifier io_notifier; + EventNotifier crw_notifier; bool force_orb_pfch; bool warned_orb_pfch; }; @@ -116,6 +123,51 @@ again: } } +static IOInstEnding vfio_ccw_handle_store(SubchDev *sch) +{ + S390CCWDevice *cdev = sch->driver_data; + VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + SCHIB *schib = &sch->curr_status; + struct ccw_schib_region *region = vcdev->schib_region; + SCHIB *s; + int ret; + + /* schib region not available so nothing else to do */ + if (!region) { + return IOINST_CC_EXPECTED; + } + + memset(region, 0, sizeof(*region)); + ret = pread(vcdev->vdev.fd, region, vcdev->schib_region_size, + vcdev->schib_region_offset); + + if (ret == -1) { + /* + * Device is probably damaged, but store subchannel does not + * have a nonzero cc defined for this scenario. Log an error, + * and presume things are otherwise fine. + */ + error_report("vfio-ccw: store region read failed with errno=%d", errno); + return IOINST_CC_EXPECTED; + } + + /* + * Selectively copy path-related bits of the SCHIB, + * rather than copying the entire struct. + */ + s = (SCHIB *)region->schib_area; + schib->pmcw.pnom = s->pmcw.pnom; + schib->pmcw.lpum = s->pmcw.lpum; + schib->pmcw.pam = s->pmcw.pam; + schib->pmcw.pom = s->pmcw.pom; + + if (s->scsw.flags & SCSW_FLAGS_MASK_PNO) { + schib->scsw.flags |= SCSW_FLAGS_MASK_PNO; + } + + return IOINST_CC_EXPECTED; +} + static int vfio_ccw_handle_clear(SubchDev *sch) { S390CCWDevice *cdev = sch->driver_data; @@ -206,6 +258,44 @@ static void vfio_ccw_reset(DeviceState *dev) ioctl(vcdev->vdev.fd, VFIO_DEVICE_RESET); } +static void vfio_ccw_crw_read(VFIOCCWDevice *vcdev) +{ + struct ccw_crw_region *region = vcdev->crw_region; + CRW crw; + int size; + + /* Keep reading CRWs as long as data is returned */ + do { + memset(region, 0, sizeof(*region)); + size = pread(vcdev->vdev.fd, region, vcdev->crw_region_size, + vcdev->crw_region_offset); + + if (size == -1) { + error_report("vfio-ccw: Read crw region failed with errno=%d", + errno); + break; + } + + if (region->crw == 0) { + /* No more CRWs to queue */ + break; + } + + memcpy(&crw, ®ion->crw, sizeof(CRW)); + + css_crw_add_to_queue(crw); + } while (1); +} + +static void vfio_ccw_crw_notifier_handler(void *opaque) +{ + VFIOCCWDevice *vcdev = opaque; + + while (event_notifier_test_and_clear(&vcdev->crw_notifier)) { + vfio_ccw_crw_read(vcdev); + } +} + static void vfio_ccw_io_notifier_handler(void *opaque) { VFIOCCWDevice *vcdev = opaque; @@ -276,22 +366,40 @@ read_err: css_inject_io_interrupt(sch); } -static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp) +static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, + unsigned int irq, + Error **errp) { VFIODevice *vdev = &vcdev->vdev; struct vfio_irq_info *irq_info; size_t argsz; int fd; + EventNotifier *notifier; + IOHandler *fd_read; + + switch (irq) { + case VFIO_CCW_IO_IRQ_INDEX: + notifier = &vcdev->io_notifier; + fd_read = vfio_ccw_io_notifier_handler; + break; + case VFIO_CCW_CRW_IRQ_INDEX: + notifier = &vcdev->crw_notifier; + fd_read = vfio_ccw_crw_notifier_handler; + break; + default: + error_setg(errp, "vfio: Unsupported device irq(%d)", irq); + return; + } - if (vdev->num_irqs < VFIO_CCW_IO_IRQ_INDEX + 1) { - error_setg(errp, "vfio: unexpected number of io irqs %u", + if (vdev->num_irqs < irq + 1) { + error_setg(errp, "vfio: unexpected number of irqs %u", vdev->num_irqs); return; } argsz = sizeof(*irq_info); irq_info = g_malloc0(argsz); - irq_info->index = VFIO_CCW_IO_IRQ_INDEX; + irq_info->index = irq; irq_info->argsz = argsz; if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, irq_info) < 0 || irq_info->count < 1) { @@ -299,37 +407,52 @@ static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp) goto out_free_info; } - if (event_notifier_init(&vcdev->io_notifier, 0)) { + if (event_notifier_init(notifier, 0)) { error_setg_errno(errp, errno, - "vfio: Unable to init event notifier for IO"); + "vfio: Unable to init event notifier for irq (%d)", + irq); goto out_free_info; } - fd = event_notifier_get_fd(&vcdev->io_notifier); - qemu_set_fd_handler(fd, vfio_ccw_io_notifier_handler, NULL, vcdev); + fd = event_notifier_get_fd(notifier); + qemu_set_fd_handler(fd, fd_read, NULL, vcdev); - if (vfio_set_irq_signaling(vdev, VFIO_CCW_IO_IRQ_INDEX, 0, + if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vcdev); - event_notifier_cleanup(&vcdev->io_notifier); + event_notifier_cleanup(notifier); } out_free_info: g_free(irq_info); } -static void vfio_ccw_unregister_io_notifier(VFIOCCWDevice *vcdev) +static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, + unsigned int irq) { Error *err = NULL; + EventNotifier *notifier; + + switch (irq) { + case VFIO_CCW_IO_IRQ_INDEX: + notifier = &vcdev->io_notifier; + break; + case VFIO_CCW_CRW_IRQ_INDEX: + notifier = &vcdev->crw_notifier; + break; + default: + error_report("vfio: Unsupported device irq(%d)", irq); + return; + } - if (vfio_set_irq_signaling(&vcdev->vdev, VFIO_CCW_IO_IRQ_INDEX, 0, + if (vfio_set_irq_signaling(&vcdev->vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); } - qemu_set_fd_handler(event_notifier_get_fd(&vcdev->io_notifier), + qemu_set_fd_handler(event_notifier_get_fd(notifier), NULL, NULL, vcdev); - event_notifier_cleanup(&vcdev->io_notifier); + event_notifier_cleanup(notifier); } static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) @@ -363,8 +486,7 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) vcdev->io_region_size = info->size; if (sizeof(*vcdev->io_region) != vcdev->io_region_size) { error_setg(errp, "vfio: Unexpected size of the I/O region"); - g_free(info); - return; + goto out_err; } vcdev->io_region_offset = info->offset; @@ -377,19 +499,53 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) vcdev->async_cmd_region_size = info->size; if (sizeof(*vcdev->async_cmd_region) != vcdev->async_cmd_region_size) { error_setg(errp, "vfio: Unexpected size of the async cmd region"); - g_free(vcdev->io_region); - g_free(info); - return; + goto out_err; } vcdev->async_cmd_region_offset = info->offset; vcdev->async_cmd_region = g_malloc0(info->size); } + ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_SCHIB, &info); + if (!ret) { + vcdev->schib_region_size = info->size; + if (sizeof(*vcdev->schib_region) != vcdev->schib_region_size) { + error_setg(errp, "vfio: Unexpected size of the schib region"); + goto out_err; + } + vcdev->schib_region_offset = info->offset; + vcdev->schib_region = g_malloc(info->size); + } + + ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_CRW, &info); + + if (!ret) { + vcdev->crw_region_size = info->size; + if (sizeof(*vcdev->crw_region) != vcdev->crw_region_size) { + error_setg(errp, "vfio: Unexpected size of the CRW region"); + goto out_err; + } + vcdev->crw_region_offset = info->offset; + vcdev->crw_region = g_malloc(info->size); + } + g_free(info); + return; + +out_err: + g_free(vcdev->crw_region); + g_free(vcdev->schib_region); + g_free(vcdev->async_cmd_region); + g_free(vcdev->io_region); + g_free(info); + return; } static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) { + g_free(vcdev->crw_region); + g_free(vcdev->schib_region); g_free(vcdev->async_cmd_region); g_free(vcdev->io_region); } @@ -499,11 +655,19 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) goto out_region_err; } - vfio_ccw_register_io_notifier(vcdev, &err); + vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err); if (err) { goto out_notifier_err; } + if (vcdev->crw_region) { + vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, &err); + if (err) { + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); + goto out_notifier_err; + } + } + return; out_notifier_err: @@ -528,7 +692,8 @@ static void vfio_ccw_unrealize(DeviceState *dev) S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); VFIOGroup *group = vcdev->vdev.group; - vfio_ccw_unregister_io_notifier(vcdev); + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); vfio_ccw_put_region(vcdev); vfio_ccw_put_device(vcdev); vfio_put_group(group); @@ -565,6 +730,7 @@ static void vfio_ccw_class_init(ObjectClass *klass, void *data) cdc->handle_request = vfio_ccw_handle_request; cdc->handle_halt = vfio_ccw_handle_halt; cdc->handle_clear = vfio_ccw_handle_clear; + cdc->handle_store = vfio_ccw_handle_store; } static const TypeInfo vfio_ccw_info = { diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c index 1541365914..5bbadadfa6 100644 --- a/hw/watchdog/cmsdk-apb-watchdog.c +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -225,6 +225,7 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset, break; case A_WDOGLOCK: s->lock = (value != WDOG_UNLOCK_VALUE); + trace_cmsdk_apb_watchdog_lock(s->lock); break; case A_WDOGITCR: if (s->is_luminary) { diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index ab94d7df50..3124ca1f1b 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -4,3 +4,4 @@ cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" 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 diff --git a/include/hw/char/ibex_uart.h b/include/hw/char/ibex_uart.h new file mode 100644 index 0000000000..2bec772615 --- /dev/null +++ b/include/hw/char/ibex_uart.h @@ -0,0 +1,110 @@ +/* + * QEMU lowRISC Ibex UART device + * + * Copyright (c) 2020 Western Digital + * + * 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 HW_IBEX_UART_H +#define HW_IBEX_UART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "qemu/timer.h" + +#define IBEX_UART_INTR_STATE 0x00 + #define INTR_STATE_TX_WATERMARK (1 << 0) + #define INTR_STATE_RX_WATERMARK (1 << 1) + #define INTR_STATE_TX_EMPTY (1 << 2) + #define INTR_STATE_RX_OVERFLOW (1 << 3) +#define IBEX_UART_INTR_ENABLE 0x04 +#define IBEX_UART_INTR_TEST 0x08 + +#define IBEX_UART_CTRL 0x0c + #define UART_CTRL_TX_ENABLE (1 << 0) + #define UART_CTRL_RX_ENABLE (1 << 1) + #define UART_CTRL_NF (1 << 2) + #define UART_CTRL_SLPBK (1 << 4) + #define UART_CTRL_LLPBK (1 << 5) + #define UART_CTRL_PARITY_EN (1 << 6) + #define UART_CTRL_PARITY_ODD (1 << 7) + #define UART_CTRL_RXBLVL (3 << 8) + #define UART_CTRL_NCO (0xFFFF << 16) + +#define IBEX_UART_STATUS 0x10 + #define UART_STATUS_TXFULL (1 << 0) + #define UART_STATUS_RXFULL (1 << 1) + #define UART_STATUS_TXEMPTY (1 << 2) + #define UART_STATUS_RXIDLE (1 << 4) + #define UART_STATUS_RXEMPTY (1 << 5) + +#define IBEX_UART_RDATA 0x14 +#define IBEX_UART_WDATA 0x18 + +#define IBEX_UART_FIFO_CTRL 0x1c + #define FIFO_CTRL_RXRST (1 << 0) + #define FIFO_CTRL_TXRST (1 << 1) + #define FIFO_CTRL_RXILVL (7 << 2) + #define FIFO_CTRL_RXILVL_SHIFT (2) + #define FIFO_CTRL_TXILVL (3 << 5) + #define FIFO_CTRL_TXILVL_SHIFT (5) + +#define IBEX_UART_FIFO_STATUS 0x20 +#define IBEX_UART_OVRD 0x24 +#define IBEX_UART_VAL 0x28 +#define IBEX_UART_TIMEOUT_CTRL 0x2c + +#define IBEX_UART_TX_FIFO_SIZE 16 + +#define TYPE_IBEX_UART "ibex-uart" +#define IBEX_UART(obj) \ + OBJECT_CHECK(IbexUartState, (obj), TYPE_IBEX_UART) + +typedef struct { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion mmio; + + uint8_t tx_fifo[IBEX_UART_TX_FIFO_SIZE]; + uint32_t tx_level; + + QEMUTimer *fifo_trigger_handle; + uint64_t char_tx_time; + + uint32_t uart_intr_state; + uint32_t uart_intr_enable; + uint32_t uart_ctrl; + uint32_t uart_status; + uint32_t uart_rdata; + uint32_t uart_fifo_ctrl; + uint32_t uart_fifo_status; + uint32_t uart_ovrd; + uint32_t uart_val; + uint32_t uart_timeout_ctrl; + + CharBackend chr; + qemu_irq tx_watermark; + qemu_irq rx_watermark; + qemu_irq tx_empty; + qemu_irq rx_overflow; +} IbexUartState; +#endif /* HW_IBEX_UART_H */ diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h new file mode 100644 index 0000000000..efdebc620a --- /dev/null +++ b/include/hw/char/renesas_sci.h @@ -0,0 +1,51 @@ +/* + * Renesas Serial Communication Interface + * + * Copyright (c) 2018 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_CHAR_RENESAS_SCI_H +#define HW_CHAR_RENESAS_SCI_H + +#include "chardev/char-fe.h" +#include "hw/sysbus.h" + +#define TYPE_RENESAS_SCI "renesas-sci" +#define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI) + +enum { + ERI = 0, + RXI = 1, + TXI = 2, + TEI = 3, + SCI_NR_IRQ = 4 +}; + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion memory; + QEMUTimer timer; + CharBackend chr; + qemu_irq irq[SCI_NR_IRQ]; + + uint8_t smr; + uint8_t brr; + uint8_t scr; + uint8_t tdr; + uint8_t ssr; + uint8_t rdr; + uint8_t scmr; + uint8_t semr; + + uint8_t read_ssr; + int64_t trtime; + int64_t rx_next; + uint64_t input_freq; +} RSCIState; + +#endif diff --git a/include/hw/i2c/arm_sbcon_i2c.h b/include/hw/i2c/arm_sbcon_i2c.h new file mode 100644 index 0000000000..5d96507ab6 --- /dev/null +++ b/include/hw/i2c/arm_sbcon_i2c.h @@ -0,0 +1,35 @@ +/* + * ARM SBCon two-wire serial bus interface (I2C bitbang) + * a.k.a. + * ARM Versatile I2C controller + * + * Copyright (c) 2006-2007 CodeSourcery. + * Copyright (c) 2012 Oskar Andero <oskar.andero@gmail.com> + * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_I2C_ARM_SBCON_H +#define HW_I2C_ARM_SBCON_H + +#include "hw/sysbus.h" +#include "hw/i2c/bitbang_i2c.h" + +#define TYPE_VERSATILE_I2C "versatile_i2c" +#define TYPE_ARM_SBCON_I2C TYPE_VERSATILE_I2C + +#define ARM_SBCON_I2C(obj) \ + OBJECT_CHECK(ArmSbconI2CState, (obj), TYPE_ARM_SBCON_I2C) + +typedef struct ArmSbconI2CState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + bitbang_i2c_interface bitbang; + int out; + int in; +} ArmSbconI2CState; + +#endif /* HW_I2C_ARM_SBCON_H */ diff --git a/include/hw/intc/ibex_plic.h b/include/hw/intc/ibex_plic.h new file mode 100644 index 0000000000..ddc7909903 --- /dev/null +++ b/include/hw/intc/ibex_plic.h @@ -0,0 +1,63 @@ +/* + * QEMU RISC-V lowRISC Ibex PLIC + * + * Copyright (c) 2020 Western Digital + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_IBEX_PLIC_H +#define HW_IBEX_PLIC_H + +#include "hw/sysbus.h" + +#define TYPE_IBEX_PLIC "ibex-plic" +#define IBEX_PLIC(obj) \ + OBJECT_CHECK(IbexPlicState, (obj), TYPE_IBEX_PLIC) + +typedef struct IbexPlicState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + + uint32_t *pending; + uint32_t *source; + uint32_t *priority; + uint32_t *enable; + uint32_t threshold; + uint32_t claim; + + /* config */ + uint32_t num_cpus; + uint32_t num_sources; + + uint32_t pending_base; + uint32_t pending_num; + + uint32_t source_base; + uint32_t source_num; + + uint32_t priority_base; + uint32_t priority_num; + + uint32_t enable_base; + uint32_t enable_num; + + uint32_t threshold_base; + + uint32_t claim_base; +} IbexPlicState; + +#endif /* HW_IBEX_PLIC_H */ diff --git a/include/hw/intc/rx_icu.h b/include/hw/intc/rx_icu.h new file mode 100644 index 0000000000..7176015cd9 --- /dev/null +++ b/include/hw/intc/rx_icu.h @@ -0,0 +1,76 @@ +/* + * RX Interrupt Control Unit + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_INTC_RX_ICU_H +#define HW_INTC_RX_ICU_H + +#include "hw/sysbus.h" + +enum TRG_MODE { + TRG_LEVEL = 0, + TRG_NEDGE = 1, /* Falling */ + TRG_PEDGE = 2, /* Raising */ + TRG_BEDGE = 3, /* Both */ +}; + +struct IRQSource { + enum TRG_MODE sense; + int level; +}; + +enum { + /* Software interrupt request */ + SWI = 27, + NR_IRQS = 256 +}; + +struct RXICUState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion memory; + struct IRQSource src[NR_IRQS]; + uint32_t nr_irqs; + uint8_t *map; + uint32_t nr_sense; + uint8_t *init_sense; + + uint8_t ir[NR_IRQS]; + uint8_t dtcer[NR_IRQS]; + uint8_t ier[NR_IRQS / 8]; + uint8_t ipr[142]; + uint8_t dmasr[4]; + uint16_t fir; + uint8_t nmisr; + uint8_t nmier; + uint8_t nmiclr; + uint8_t nmicr; + int16_t req_irq; + qemu_irq _irq; + qemu_irq _fir; + qemu_irq _swi; +}; +typedef struct RXICUState RXICUState; + +#define TYPE_RX_ICU "rx-icu" +#define RX_ICU(obj) OBJECT_CHECK(RXICUState, (obj), TYPE_RX_ICU) + +#endif /* RX_ICU_H */ diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h index a4b6499444..8f29b9cbbf 100644 --- a/include/hw/riscv/opentitan.h +++ b/include/hw/riscv/opentitan.h @@ -20,6 +20,8 @@ #define HW_OPENTITAN_H #include "hw/riscv/riscv_hart.h" +#include "hw/intc/ibex_plic.h" +#include "hw/char/ibex_uart.h" #define TYPE_RISCV_IBEX_SOC "riscv.lowrisc.ibex.soc" #define RISCV_IBEX_SOC(obj) \ @@ -31,6 +33,9 @@ typedef struct LowRISCIbexSoCState { /*< public >*/ RISCVHartArrayState cpus; + IbexPlicState plic; + IbexUartState uart; + MemoryRegion flash_mem; MemoryRegion rom; } LowRISCIbexSoCState; @@ -65,4 +70,15 @@ enum { IBEX_PADCTRL, }; +enum { + IBEX_UART_RX_PARITY_ERR_IRQ = 0x28, + IBEX_UART_RX_TIMEOUT_IRQ = 0x27, + IBEX_UART_RX_BREAK_ERR_IRQ = 0x26, + IBEX_UART_RX_FRAME_ERR_IRQ = 0x25, + IBEX_UART_RX_OVERFLOW_IRQ = 0x24, + IBEX_UART_TX_EMPTY_IRQ = 0x23, + IBEX_UART_RX_WATERMARK_IRQ = 0x22, + IBEX_UART_TX_WATERMARK_IRQ = 0x21, +}; + #endif diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index d386ea9223..637414130b 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -45,6 +45,7 @@ typedef struct SiFiveEState { /*< public >*/ SiFiveESoCState soc; + bool revb; } SiFiveEState; #define TYPE_RISCV_E_MACHINE MACHINE_TYPE_NAME("sifive_e") diff --git a/include/hw/riscv/sifive_gpio.h b/include/hw/riscv/sifive_gpio.h index fce03d6c41..cf12fcfd62 100644 --- a/include/hw/riscv/sifive_gpio.h +++ b/include/hw/riscv/sifive_gpio.h @@ -1,5 +1,5 @@ /* - * sifive System-on-Chip general purpose input/output register definition + * SiFive System-on-Chip general purpose input/output register definition * * Copyright 2019 AdaCore * @@ -10,10 +10,12 @@ * This code is licensed under the GPL version 2 or later. See * the COPYING file in the top-level directory. */ + #ifndef SIFIVE_GPIO_H #define SIFIVE_GPIO_H #include "hw/sysbus.h" + #define TYPE_SIFIVE_GPIO "sifive_soc.gpio" #define SIFIVE_GPIO(obj) OBJECT_CHECK(SIFIVEGPIOState, (obj), TYPE_SIFIVE_GPIO) @@ -67,6 +69,8 @@ typedef struct SIFIVEGPIOState { uint32_t in; uint32_t in_mask; + /* config */ + uint32_t ngpio; } SIFIVEGPIOState; -#endif +#endif /* SIFIVE_GPIO_H */ diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 5f62cf5f85..aba4d0181f 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -22,6 +22,7 @@ #include "hw/net/cadence_gem.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_cpu.h" +#include "hw/riscv/sifive_gpio.h" #include "hw/riscv/sifive_u_prci.h" #include "hw/riscv/sifive_u_otp.h" @@ -40,6 +41,7 @@ typedef struct SiFiveUSoCState { RISCVHartArrayState u_cpus; DeviceState *plic; SiFiveUPRCIState prci; + SIFIVEGPIOState gpio; SiFiveUOTPState otp; CadenceGEMState gem; @@ -61,6 +63,7 @@ typedef struct SiFiveUState { int fdt_size; bool start_in_flash; + uint32_t msel; uint32_t serial; } SiFiveUState; @@ -73,7 +76,9 @@ enum { SIFIVE_U_PRCI, SIFIVE_U_UART0, SIFIVE_U_UART1, + SIFIVE_U_GPIO, SIFIVE_U_OTP, + SIFIVE_U_DMC, SIFIVE_U_FLASH0, SIFIVE_U_DRAM, SIFIVE_U_GEM, @@ -83,6 +88,22 @@ enum { enum { SIFIVE_U_UART0_IRQ = 4, SIFIVE_U_UART1_IRQ = 5, + SIFIVE_U_GPIO_IRQ0 = 7, + SIFIVE_U_GPIO_IRQ1 = 8, + SIFIVE_U_GPIO_IRQ2 = 9, + SIFIVE_U_GPIO_IRQ3 = 10, + SIFIVE_U_GPIO_IRQ4 = 11, + SIFIVE_U_GPIO_IRQ5 = 12, + SIFIVE_U_GPIO_IRQ6 = 13, + SIFIVE_U_GPIO_IRQ7 = 14, + SIFIVE_U_GPIO_IRQ8 = 15, + SIFIVE_U_GPIO_IRQ9 = 16, + SIFIVE_U_GPIO_IRQ10 = 17, + SIFIVE_U_GPIO_IRQ11 = 18, + SIFIVE_U_GPIO_IRQ12 = 19, + SIFIVE_U_GPIO_IRQ13 = 20, + SIFIVE_U_GPIO_IRQ14 = 21, + SIFIVE_U_GPIO_IRQ15 = 22, SIFIVE_U_GEM_IRQ = 0x35 }; @@ -91,6 +112,12 @@ enum { SIFIVE_U_RTCCLK_FREQ = 1000000 }; +enum { + MSEL_MEMMAP_QSPI0_FLASH = 1, + MSEL_L2LIM_QSPI0_FLASH = 6, + MSEL_L2LIM_QSPI2_SD = 11 +}; + #define SIFIVE_U_MANAGEMENT_CPU_COUNT 1 #define SIFIVE_U_COMPUTE_CPU_COUNT 4 diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h new file mode 100644 index 0000000000..aa94758c27 --- /dev/null +++ b/include/hw/rx/rx62n.h @@ -0,0 +1,76 @@ +/* + * RX62N MCU Object + * + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware + * (Rev.1.40 R01UH0033EJ0140) + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_RX_RX62N_MCU_H +#define HW_RX_RX62N_MCU_H + +#include "target/rx/cpu.h" +#include "hw/intc/rx_icu.h" +#include "hw/timer/renesas_tmr.h" +#include "hw/timer/renesas_cmt.h" +#include "hw/char/renesas_sci.h" +#include "qemu/units.h" + +#define TYPE_RX62N_MCU "rx62n-mcu" +#define RX62N_MCU(obj) OBJECT_CHECK(RX62NState, (obj), TYPE_RX62N_MCU) + +#define TYPE_R5F562N7_MCU "r5f562n7-mcu" +#define TYPE_R5F562N8_MCU "r5f562n8-mcu" + +#define EXT_CS_BASE 0x01000000 +#define VECTOR_TABLE_BASE 0xffffff80 +#define RX62N_CFLASH_BASE 0xfff80000 + +#define RX62N_NR_TMR 2 +#define RX62N_NR_CMT 2 +#define RX62N_NR_SCI 6 + +typedef struct RX62NState { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + RXCPU cpu; + RXICUState icu; + RTMRState tmr[RX62N_NR_TMR]; + RCMTState cmt[RX62N_NR_CMT]; + RSCIState sci[RX62N_NR_SCI]; + + MemoryRegion *sysmem; + bool kernel; + + MemoryRegion iram; + MemoryRegion iomem1; + MemoryRegion d_flash; + MemoryRegion iomem2; + MemoryRegion iomem3; + MemoryRegion c_flash; + qemu_irq irq[NR_IRQS]; + + /* Input Clock (XTAL) frequency */ + uint32_t xtal_freq_hz; + /* Peripheral Module Clock frequency */ + uint32_t pclk_freq_hz; +} RX62NState; + +#endif diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index f46bcafb16..08c869ab0a 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -205,6 +205,7 @@ void copy_scsw_to_guest(SCSW *dest, const SCSW *src); void css_inject_io_interrupt(SubchDev *sch); void css_reset(void); void css_reset_sch(SubchDev *sch); +void css_crw_add_to_queue(CRW crw); void css_queue_crw(uint8_t rsc, uint8_t erc, int solicited, int chain, uint16_t rsid); void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, @@ -218,6 +219,7 @@ IOInstEnding do_subchannel_work_passthrough(SubchDev *sub); int s390_ccw_halt(SubchDev *sch); int s390_ccw_clear(SubchDev *sch); +IOInstEnding s390_ccw_store(SubchDev *sch); typedef enum { CSS_IO_ADAPTER_VIRTIO = 0, @@ -242,7 +244,7 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid); bool css_subch_visible(SubchDev *sch); void css_conditional_io_interrupt(SubchDev *sch); -int css_do_stsch(SubchDev *sch, SCHIB *schib); +IOInstEnding css_do_stsch(SubchDev *sch, SCHIB *schib); bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid); IOInstEnding css_do_msch(SubchDev *sch, const SCHIB *schib); IOInstEnding css_do_xsch(SubchDev *sch); diff --git a/include/hw/s390x/s390-ccw.h b/include/hw/s390x/s390-ccw.h index 7f27bc2f53..d8e08b5f4c 100644 --- a/include/hw/s390x/s390-ccw.h +++ b/include/hw/s390x/s390-ccw.h @@ -37,6 +37,7 @@ typedef struct S390CCWDeviceClass { IOInstEnding (*handle_request) (SubchDev *sch); int (*handle_halt) (SubchDev *sch); int (*handle_clear) (SubchDev *sch); + IOInstEnding (*handle_store) (SubchDev *sch); } S390CCWDeviceClass; #endif diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h index 767a2df7e2..93f464bf4c 100644 --- a/include/hw/sh4/sh.h +++ b/include/hw/sh4/sh.h @@ -10,9 +10,8 @@ /* sh7750.c */ struct SH7750State; -struct MemoryRegion; -struct SH7750State *sh7750_init(SuperHCPU *cpu, struct MemoryRegion *sysmem); +struct SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem); typedef struct { /* The callback will be triggered if any of the designated lines change */ @@ -28,15 +27,6 @@ typedef struct { int sh7750_register_io_device(struct SH7750State *s, sh7750_io_device * device); -/* sh_timer.c */ -#define TMU012_FEAT_TOCR (1 << 0) -#define TMU012_FEAT_3CHAN (1 << 1) -#define TMU012_FEAT_EXTCLK (1 << 2) -void tmu012_init(struct 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); - /* sh_serial.c */ #define SH_SERIAL_FEAT_SCIF (1 << 0) diff --git a/include/hw/timer/renesas_cmt.h b/include/hw/timer/renesas_cmt.h new file mode 100644 index 0000000000..e28a15cb38 --- /dev/null +++ b/include/hw/timer/renesas_cmt.h @@ -0,0 +1,40 @@ +/* + * Renesas Compare-match timer Object + * + * Copyright (c) 2019 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_TIMER_RENESAS_CMT_H +#define HW_TIMER_RENESAS_CMT_H + +#include "qemu/timer.h" +#include "hw/sysbus.h" + +#define TYPE_RENESAS_CMT "renesas-cmt" +#define RCMT(obj) OBJECT_CHECK(RCMTState, (obj), TYPE_RENESAS_CMT) + +enum { + CMT_CH = 2, + CMT_NR_IRQ = 1 * CMT_CH +}; + +typedef struct RCMTState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint64_t input_freq; + MemoryRegion memory; + + uint16_t cmstr; + uint16_t cmcr[CMT_CH]; + uint16_t cmcnt[CMT_CH]; + uint16_t cmcor[CMT_CH]; + int64_t tick[CMT_CH]; + qemu_irq cmi[CMT_CH]; + QEMUTimer timer[CMT_CH]; +} RCMTState; + +#endif diff --git a/include/hw/timer/renesas_tmr.h b/include/hw/timer/renesas_tmr.h new file mode 100644 index 0000000000..cf3baa7a28 --- /dev/null +++ b/include/hw/timer/renesas_tmr.h @@ -0,0 +1,55 @@ +/* + * Renesas 8bit timer Object + * + * Copyright (c) 2018 Yoshinori Sato + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_TIMER_RENESAS_TMR_H +#define HW_TIMER_RENESAS_TMR_H + +#include "qemu/timer.h" +#include "hw/sysbus.h" + +#define TYPE_RENESAS_TMR "renesas-tmr" +#define RTMR(obj) OBJECT_CHECK(RTMRState, (obj), TYPE_RENESAS_TMR) + +enum timer_event { + cmia = 0, + cmib = 1, + ovi = 2, + none = 3, + TMR_NR_EVENTS = 4 +}; + +enum { + TMR_CH = 2, + TMR_NR_IRQ = 3 * TMR_CH +}; + +typedef struct RTMRState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint64_t input_freq; + MemoryRegion memory; + + int64_t tick; + uint8_t tcnt[TMR_CH]; + uint8_t tcora[TMR_CH]; + uint8_t tcorb[TMR_CH]; + uint8_t tcr[TMR_CH]; + uint8_t tccr[TMR_CH]; + uint8_t tcor[TMR_CH]; + uint8_t tcsr[TMR_CH]; + int64_t div_round[TMR_CH]; + uint8_t next[TMR_CH]; + qemu_irq cmia[TMR_CH]; + qemu_irq cmib[TMR_CH]; + qemu_irq ovi[TMR_CH]; + QEMUTimer timer[TMR_CH]; +} RTMRState; + +#endif diff --git a/include/hw/timer/tmu012.h b/include/hw/timer/tmu012.h new file mode 100644 index 0000000000..808ed8de1d --- /dev/null +++ b/include/hw/timer/tmu012.h @@ -0,0 +1,23 @@ +/* + * SuperH Timer + * + * Copyright (c) 2007 Magnus Damm + * + * This code is licensed under the GPL. + */ + +#ifndef HW_TIMER_TMU012_H +#define HW_TIMER_TMU012_H + +#include "exec/hwaddr.h" + +#define TMU012_FEAT_TOCR (1 << 0) +#define TMU012_FEAT_3CHAN (1 << 1) +#define TMU012_FEAT_EXTCLK (1 << 2) + +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); + +#endif diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h index 90604a8fb7..07877d3295 100644 --- a/include/standard-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -31,6 +31,7 @@ #define KVM_FEATURE_PV_SEND_IPI 11 #define KVM_FEATURE_POLL_CONTROL 12 #define KVM_FEATURE_PV_SCHED_YIELD 13 +#define KVM_FEATURE_ASYNC_PF_INT 14 #define KVM_HINTS_REALTIME 0 @@ -50,6 +51,8 @@ #define MSR_KVM_STEAL_TIME 0x4b564d03 #define MSR_KVM_PV_EOI_EN 0x4b564d04 #define MSR_KVM_POLL_CONTROL 0x4b564d05 +#define MSR_KVM_ASYNC_PF_INT 0x4b564d06 +#define MSR_KVM_ASYNC_PF_ACK 0x4b564d07 struct kvm_steal_time { uint64_t steal; @@ -81,6 +84,11 @@ struct kvm_clock_pairing { #define KVM_ASYNC_PF_ENABLED (1 << 0) #define KVM_ASYNC_PF_SEND_ALWAYS (1 << 1) #define KVM_ASYNC_PF_DELIVERY_AS_PF_VMEXIT (1 << 2) +#define KVM_ASYNC_PF_DELIVERY_AS_INT (1 << 3) + +/* MSR_KVM_ASYNC_PF_INT */ +#define KVM_ASYNC_PF_VEC_MASK GENMASK(7, 0) + /* Operations for KVM_HC_MMU_OP */ #define KVM_MMU_OP_WRITE_PTE 1 @@ -112,8 +120,13 @@ struct kvm_mmu_op_release_pt { #define KVM_PV_REASON_PAGE_READY 2 struct kvm_vcpu_pv_apf_data { - uint32_t reason; - uint8_t pad[60]; + /* Used for 'page not present' events delivered via #PF */ + uint32_t flags; + + /* Used for 'page ready' events delivered via interrupt notification */ + uint32_t token; + + uint8_t pad[56]; uint32_t enabled; }; diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index 66e838074c..909a66753c 100644 --- a/include/standard-headers/drm/drm_fourcc.h +++ b/include/standard-headers/drm/drm_fourcc.h @@ -353,9 +353,12 @@ extern "C" { * a platform-dependent stride. On top of that the memory can apply * platform-depending swizzling of some higher address bits into bit6. * - * This format is highly platforms specific and not useful for cross-driver - * sharing. It exists since on a given platform it does uniquely identify the - * layout in a simple way for i915-specific userspace. + * Note that this layout is only accurate on intel gen 8+ or valleyview chipsets. + * On earlier platforms the is highly platforms specific and not useful for + * cross-driver sharing. It exists since on a given platform it does uniquely + * identify the layout in a simple way for i915-specific userspace, which + * facilitated conversion of userspace to modifiers. Additionally the exact + * format on some really old platforms is not known. */ #define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) @@ -368,9 +371,12 @@ extern "C" { * memory can apply platform-depending swizzling of some higher address bits * into bit6. * - * This format is highly platforms specific and not useful for cross-driver - * sharing. It exists since on a given platform it does uniquely identify the - * layout in a simple way for i915-specific userspace. + * Note that this layout is only accurate on intel gen 8+ or valleyview chipsets. + * On earlier platforms the is highly platforms specific and not useful for + * cross-driver sharing. It exists since on a given platform it does uniquely + * identify the layout in a simple way for i915-specific userspace, which + * facilitated conversion of userspace to modifiers. Additionally the exact + * format on some really old platforms is not known. */ #define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) @@ -520,7 +526,113 @@ extern "C" { #define DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED fourcc_mod_code(NVIDIA, 1) /* - * 16Bx2 Block Linear layout, used by desktop GPUs, and Tegra K1 and later + * Generalized Block Linear layout, used by desktop GPUs starting with NV50/G80, + * and Tegra GPUs starting with Tegra K1. + * + * Pixels are arranged in Groups of Bytes (GOBs). GOB size and layout varies + * based on the architecture generation. GOBs themselves are then arranged in + * 3D blocks, with the block dimensions (in terms of GOBs) always being a power + * of two, and hence expressible as their log2 equivalent (E.g., "2" represents + * a block depth or height of "4"). + * + * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format + * in full detail. + * + * Macro + * Bits Param Description + * ---- ----- ----------------------------------------------------------------- + * + * 3:0 h log2(height) of each block, in GOBs. Placed here for + * compatibility with the existing + * DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK()-based modifiers. + * + * 4:4 - Must be 1, to indicate block-linear layout. Necessary for + * compatibility with the existing + * DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK()-based modifiers. + * + * 8:5 - Reserved (To support 3D-surfaces with variable log2(depth) block + * size). Must be zero. + * + * Note there is no log2(width) parameter. Some portions of the + * hardware support a block width of two gobs, but it is impractical + * to use due to lack of support elsewhere, and has no known + * benefits. + * + * 11:9 - Reserved (To support 2D-array textures with variable array stride + * in blocks, specified via log2(tile width in blocks)). Must be + * zero. + * + * 19:12 k Page Kind. This value directly maps to a field in the page + * tables of all GPUs >= NV50. It affects the exact layout of bits + * in memory and can be derived from the tuple + * + * (format, GPU model, compression type, samples per pixel) + * + * Where compression type is defined below. If GPU model were + * implied by the format modifier, format, or memory buffer, page + * kind would not need to be included in the modifier itself, but + * since the modifier should define the layout of the associated + * memory buffer independent from any device or other context, it + * must be included here. + * + * 21:20 g GOB Height and Page Kind Generation. The height of a GOB changed + * starting with Fermi GPUs. Additionally, the mapping between page + * kind and bit layout has changed at various points. + * + * 0 = Gob Height 8, Fermi - Volta, Tegra K1+ Page Kind mapping + * 1 = Gob Height 4, G80 - GT2XX Page Kind mapping + * 2 = Gob Height 8, Turing+ Page Kind mapping + * 3 = Reserved for future use. + * + * 22:22 s Sector layout. On Tegra GPUs prior to Xavier, there is a further + * bit remapping step that occurs at an even lower level than the + * page kind and block linear swizzles. This causes the layout of + * surfaces mapped in those SOC's GPUs to be incompatible with the + * equivalent mapping on other GPUs in the same system. + * + * 0 = Tegra K1 - Tegra Parker/TX2 Layout. + * 1 = Desktop GPU and Tegra Xavier+ Layout + * + * 25:23 c Lossless Framebuffer Compression type. + * + * 0 = none + * 1 = ROP/3D, layout 1, exact compression format implied by Page + * Kind field + * 2 = ROP/3D, layout 2, exact compression format implied by Page + * Kind field + * 3 = CDE horizontal + * 4 = CDE vertical + * 5 = Reserved for future use + * 6 = Reserved for future use + * 7 = Reserved for future use + * + * 55:25 - Reserved for future use. Must be zero. + */ +#define DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(c, s, g, k, h) \ + fourcc_mod_code(NVIDIA, (0x10 | \ + ((h) & 0xf) | \ + (((k) & 0xff) << 12) | \ + (((g) & 0x3) << 20) | \ + (((s) & 0x1) << 22) | \ + (((c) & 0x7) << 23))) + +/* To grandfather in prior block linear format modifiers to the above layout, + * the page kind "0", which corresponds to "pitch/linear" and hence is unusable + * with block-linear layouts, is remapped within drivers to the value 0xfe, + * which corresponds to the "generic" kind used for simple single-sample + * uncompressed color formats on Fermi - Volta GPUs. + */ +static inline uint64_t +drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) +{ + if (!(modifier & 0x10) || (modifier & (0xff << 12))) + return modifier; + else + return modifier | (0xfe << 12); +} + +/* + * 16Bx2 Block Linear layout, used by Tegra K1 and later * * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked * vertically by a power of 2 (1 to 32 GOBs) to form a block. @@ -541,20 +653,20 @@ extern "C" { * in full detail. */ #define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(v) \ - fourcc_mod_code(NVIDIA, 0x10 | ((v) & 0xf)) + DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 0, 0, 0, (v)) #define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB \ - fourcc_mod_code(NVIDIA, 0x10) + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0) #define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB \ - fourcc_mod_code(NVIDIA, 0x11) + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1) #define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB \ - fourcc_mod_code(NVIDIA, 0x12) + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2) #define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB \ - fourcc_mod_code(NVIDIA, 0x13) + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3) #define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB \ - fourcc_mod_code(NVIDIA, 0x14) + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4) #define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \ - fourcc_mod_code(NVIDIA, 0x15) + DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5) /* * Some Broadcom modifiers take parameters, for example the number of diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 1200890c86..fd8d2cccfe 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -1666,6 +1666,18 @@ static inline int ethtool_validate_duplex(uint8_t duplex) return 0; } +#define MASTER_SLAVE_CFG_UNSUPPORTED 0 +#define MASTER_SLAVE_CFG_UNKNOWN 1 +#define MASTER_SLAVE_CFG_MASTER_PREFERRED 2 +#define MASTER_SLAVE_CFG_SLAVE_PREFERRED 3 +#define MASTER_SLAVE_CFG_MASTER_FORCE 4 +#define MASTER_SLAVE_CFG_SLAVE_FORCE 5 +#define MASTER_SLAVE_STATE_UNSUPPORTED 0 +#define MASTER_SLAVE_STATE_UNKNOWN 1 +#define MASTER_SLAVE_STATE_MASTER 2 +#define MASTER_SLAVE_STATE_SLAVE 3 +#define MASTER_SLAVE_STATE_ERR 4 + /* Which connector port. */ #define PORT_TP 0x00 #define PORT_AUI 0x01 @@ -1904,7 +1916,9 @@ struct ethtool_link_settings { uint8_t eth_tp_mdix_ctrl; int8_t link_mode_masks_nwords; uint8_t transceiver; - uint8_t reserved1[3]; + uint8_t master_slave_cfg; + uint8_t master_slave_state; + uint8_t reserved1[1]; uint32_t reserved[7]; uint32_t link_mode_masks[0]; /* layout of link_mode_masks fields: diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h index ecc27a1740..b052355ac7 100644 --- a/include/standard-headers/linux/virtio_ids.h +++ b/include/standard-headers/linux/virtio_ids.h @@ -44,6 +44,7 @@ #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */ #define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */ +#define VIRTIO_ID_MEM 24 /* virtio mem */ #define VIRTIO_ID_FS 26 /* virtio filesystem */ #define VIRTIO_ID_PMEM 27 /* virtio pmem */ #define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */ diff --git a/include/standard-headers/linux/virtio_mem.h b/include/standard-headers/linux/virtio_mem.h new file mode 100644 index 0000000000..05e5ade75d --- /dev/null +++ b/include/standard-headers/linux/virtio_mem.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Virtio Mem Device + * + * Copyright Red Hat, Inc. 2020 + * + * Authors: + * David Hildenbrand <david@redhat.com> + * + * This header is BSD licensed so anyone can use the definitions + * to implement compatible drivers/servers: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LINUX_VIRTIO_MEM_H +#define _LINUX_VIRTIO_MEM_H + +#include "standard-headers/linux/types.h" +#include "standard-headers/linux/virtio_types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" + +/* + * Each virtio-mem device manages a dedicated region in physical address + * space. Each device can belong to a single NUMA node, multiple devices + * for a single NUMA node are possible. A virtio-mem device is like a + * "resizable DIMM" consisting of small memory blocks that can be plugged + * or unplugged. The device driver is responsible for (un)plugging memory + * blocks on demand. + * + * Virtio-mem devices can only operate on their assigned memory region in + * order to (un)plug memory. A device cannot (un)plug memory belonging to + * other devices. + * + * The "region_size" corresponds to the maximum amount of memory that can + * be provided by a device. The "size" corresponds to the amount of memory + * that is currently plugged. "requested_size" corresponds to a request + * from the device to the device driver to (un)plug blocks. The + * device driver should try to (un)plug blocks in order to reach the + * "requested_size". It is impossible to plug more memory than requested. + * + * The "usable_region_size" represents the memory region that can actually + * be used to (un)plug memory. It is always at least as big as the + * "requested_size" and will grow dynamically. It will only shrink when + * explicitly triggered (VIRTIO_MEM_REQ_UNPLUG). + * + * There are no guarantees what will happen if unplugged memory is + * read/written. Such memory should, in general, not be touched. E.g., + * even writing might succeed, but the values will simply be discarded at + * random points in time. + * + * It can happen that the device cannot process a request, because it is + * busy. The device driver has to retry later. + * + * Usually, during system resets all memory will get unplugged, so the + * device driver can start with a clean state. However, in specific + * scenarios (if the device is busy) it can happen that the device still + * has memory plugged. The device driver can request to unplug all memory + * (VIRTIO_MEM_REQ_UNPLUG) - which might take a while to succeed if the + * device is busy. + */ + +/* --- virtio-mem: feature bits --- */ + +/* node_id is an ACPI PXM and is valid */ +#define VIRTIO_MEM_F_ACPI_PXM 0 + + +/* --- virtio-mem: guest -> host requests --- */ + +/* request to plug memory blocks */ +#define VIRTIO_MEM_REQ_PLUG 0 +/* request to unplug memory blocks */ +#define VIRTIO_MEM_REQ_UNPLUG 1 +/* request to unplug all blocks and shrink the usable size */ +#define VIRTIO_MEM_REQ_UNPLUG_ALL 2 +/* request information about the plugged state of memory blocks */ +#define VIRTIO_MEM_REQ_STATE 3 + +struct virtio_mem_req_plug { + __virtio64 addr; + __virtio16 nb_blocks; + __virtio16 padding[3]; +}; + +struct virtio_mem_req_unplug { + __virtio64 addr; + __virtio16 nb_blocks; + __virtio16 padding[3]; +}; + +struct virtio_mem_req_state { + __virtio64 addr; + __virtio16 nb_blocks; + __virtio16 padding[3]; +}; + +struct virtio_mem_req { + __virtio16 type; + __virtio16 padding[3]; + + union { + struct virtio_mem_req_plug plug; + struct virtio_mem_req_unplug unplug; + struct virtio_mem_req_state state; + } u; +}; + + +/* --- virtio-mem: host -> guest response --- */ + +/* + * Request processed successfully, applicable for + * - VIRTIO_MEM_REQ_PLUG + * - VIRTIO_MEM_REQ_UNPLUG + * - VIRTIO_MEM_REQ_UNPLUG_ALL + * - VIRTIO_MEM_REQ_STATE + */ +#define VIRTIO_MEM_RESP_ACK 0 +/* + * Request denied - e.g. trying to plug more than requested, applicable for + * - VIRTIO_MEM_REQ_PLUG + */ +#define VIRTIO_MEM_RESP_NACK 1 +/* + * Request cannot be processed right now, try again later, applicable for + * - VIRTIO_MEM_REQ_PLUG + * - VIRTIO_MEM_REQ_UNPLUG + * - VIRTIO_MEM_REQ_UNPLUG_ALL + */ +#define VIRTIO_MEM_RESP_BUSY 2 +/* + * Error in request (e.g. addresses/alignment), applicable for + * - VIRTIO_MEM_REQ_PLUG + * - VIRTIO_MEM_REQ_UNPLUG + * - VIRTIO_MEM_REQ_STATE + */ +#define VIRTIO_MEM_RESP_ERROR 3 + + +/* State of memory blocks is "plugged" */ +#define VIRTIO_MEM_STATE_PLUGGED 0 +/* State of memory blocks is "unplugged" */ +#define VIRTIO_MEM_STATE_UNPLUGGED 1 +/* State of memory blocks is "mixed" */ +#define VIRTIO_MEM_STATE_MIXED 2 + +struct virtio_mem_resp_state { + __virtio16 state; +}; + +struct virtio_mem_resp { + __virtio16 type; + __virtio16 padding[3]; + + union { + struct virtio_mem_resp_state state; + } u; +}; + +/* --- virtio-mem: configuration --- */ + +struct virtio_mem_config { + /* Block size and alignment. Cannot change. */ + uint64_t block_size; + /* Valid with VIRTIO_MEM_F_ACPI_PXM. Cannot change. */ + uint16_t node_id; + uint8_t padding[6]; + /* Start address of the memory region. Cannot change. */ + uint64_t addr; + /* Region size (maximum). Cannot change. */ + uint64_t region_size; + /* + * Currently usable region size. Can grow up to region_size. Can + * shrink due to VIRTIO_MEM_REQ_UNPLUG_ALL (in which case no config + * update will be sent). + */ + uint64_t usable_region_size; + /* + * Currently used size. Changes due to plug/unplug requests, but no + * config updates will be sent. + */ + uint64_t plugged_size; + /* Requested size. New plug requests cannot exceed it. Can change. */ + uint64_t requested_size; +}; + +#endif /* _LINUX_VIRTIO_MEM_H */ diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h index f230fed479..0fa0e1067f 100644 --- a/include/standard-headers/linux/virtio_ring.h +++ b/include/standard-headers/linux/virtio_ring.h @@ -84,6 +84,13 @@ * at the end of the used ring. Guest should ignore the used->flags field. */ #define VIRTIO_RING_F_EVENT_IDX 29 +/* Alignment requirements for vring elements. + * When using pre-virtio 1.0 layout, these fall out naturally. + */ +#define VRING_AVAIL_ALIGN_SIZE 2 +#define VRING_USED_ALIGN_SIZE 4 +#define VRING_DESC_ALIGN_SIZE 16 + /* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc { /* Address (guest-physical). */ @@ -110,28 +117,47 @@ struct vring_used_elem { __virtio32 len; }; +typedef struct vring_used_elem __attribute__((aligned(VRING_USED_ALIGN_SIZE))) + vring_used_elem_t; + struct vring_used { __virtio16 flags; __virtio16 idx; - struct vring_used_elem ring[]; + vring_used_elem_t ring[]; }; +/* + * The ring element addresses are passed between components with different + * alignments assumptions. Thus, we might need to decrease the compiler-selected + * alignment, and so must use a typedef to make sure the aligned attribute + * actually takes hold: + * + * https://gcc.gnu.org/onlinedocs//gcc/Common-Type-Attributes.html#Common-Type-Attributes + * + * When used on a struct, or struct member, the aligned attribute can only + * increase the alignment; in order to decrease it, the packed attribute must + * be specified as well. When used as part of a typedef, the aligned attribute + * can both increase and decrease alignment, and specifying the packed + * attribute generates a warning. + */ +typedef struct vring_desc __attribute__((aligned(VRING_DESC_ALIGN_SIZE))) + vring_desc_t; +typedef struct vring_avail __attribute__((aligned(VRING_AVAIL_ALIGN_SIZE))) + vring_avail_t; +typedef struct vring_used __attribute__((aligned(VRING_USED_ALIGN_SIZE))) + vring_used_t; + struct vring { unsigned int num; - struct vring_desc *desc; + vring_desc_t *desc; - struct vring_avail *avail; + vring_avail_t *avail; - struct vring_used *used; + vring_used_t *used; }; -/* Alignment requirements for vring elements. - * When using pre-virtio 1.0 layout, these fall out naturally. - */ -#define VRING_AVAIL_ALIGN_SIZE 2 -#define VRING_USED_ALIGN_SIZE 4 -#define VRING_DESC_ALIGN_SIZE 16 +#ifndef VIRTIO_RING_NO_LEGACY /* The standard layout for the ring is a continuous chunk of memory which looks * like this. We assume num is a power of 2. @@ -179,6 +205,8 @@ static inline unsigned vring_size(unsigned int num, unsigned long align) + sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num; } +#endif /* VIRTIO_RING_NO_LEGACY */ + /* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */ /* Assuming a given event_idx value from the other side, if * we have just incremented index from old to new_idx, diff --git a/linux-headers/asm-arm64/mman.h b/linux-headers/asm-arm64/mman.h index 8eebf89f5a..e94b9af859 100644 --- a/linux-headers/asm-arm64/mman.h +++ b/linux-headers/asm-arm64/mman.h @@ -1 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_MMAN_H +#define __ASM_MMAN_H + #include <asm-generic/mman.h> + +#define PROT_BTI 0x10 /* BTI guarded page */ + +#endif /* ! _UAPI__ASM_MMAN_H */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 3a3201e461..f4a01305d9 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -855,9 +855,11 @@ __SYSCALL(__NR_clone3, sys_clone3) __SYSCALL(__NR_openat2, sys_openat2) #define __NR_pidfd_getfd 438 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd) +#define __NR_faccessat2 439 +__SYSCALL(__NR_faccessat2, sys_faccessat2) #undef __NR_syscalls -#define __NR_syscalls 439 +#define __NR_syscalls 440 /* * 32 bit systems traditionally used different diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h index aec9f6081a..3b9eda7e7d 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -367,6 +367,7 @@ #define __NR_clone3 (__NR_Linux + 435) #define __NR_openat2 (__NR_Linux + 437) #define __NR_pidfd_getfd (__NR_Linux + 438) +#define __NR_faccessat2 (__NR_Linux + 439) #endif /* _ASM_MIPS_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h index 1c75d83df5..9cdf9b6c60 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -343,6 +343,7 @@ #define __NR_clone3 (__NR_Linux + 435) #define __NR_openat2 (__NR_Linux + 437) #define __NR_pidfd_getfd (__NR_Linux + 438) +#define __NR_faccessat2 (__NR_Linux + 439) #endif /* _ASM_MIPS_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h index 660716e240..e3e5e238f0 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -413,6 +413,7 @@ #define __NR_clone3 (__NR_Linux + 435) #define __NR_openat2 (__NR_Linux + 437) #define __NR_pidfd_getfd (__NR_Linux + 438) +#define __NR_faccessat2 (__NR_Linux + 439) #endif /* _ASM_MIPS_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index 4ba8e32f73..862edb7448 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -420,6 +420,7 @@ #define __NR_clone3 435 #define __NR_openat2 437 #define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 #endif /* _ASM_POWERPC_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h index ac20bb4f95..f553224ce4 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -392,6 +392,7 @@ #define __NR_clone3 435 #define __NR_openat2 437 #define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 #endif /* _ASM_POWERPC_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index e4a6b654f1..e08233c0c3 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -410,5 +410,6 @@ #define __NR_clone3 435 #define __NR_openat2 437 #define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 472f732956..560e19ae2b 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -358,5 +358,6 @@ #define __NR_clone3 435 #define __NR_openat2 437 #define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 3f3f780c8c..17c5a038f4 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -385,32 +385,48 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4) #define KVM_STATE_NESTED_FORMAT_VMX 0 -#define KVM_STATE_NESTED_FORMAT_SVM 1 /* unused */ +#define KVM_STATE_NESTED_FORMAT_SVM 1 #define KVM_STATE_NESTED_GUEST_MODE 0x00000001 #define KVM_STATE_NESTED_RUN_PENDING 0x00000002 #define KVM_STATE_NESTED_EVMCS 0x00000004 #define KVM_STATE_NESTED_MTF_PENDING 0x00000008 +#define KVM_STATE_NESTED_GIF_SET 0x00000100 #define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001 #define KVM_STATE_NESTED_SMM_VMXON 0x00000002 #define KVM_STATE_NESTED_VMX_VMCS_SIZE 0x1000 +#define KVM_STATE_NESTED_SVM_VMCB_SIZE 0x1000 + +#define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001 + struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; __u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; }; struct kvm_vmx_nested_state_hdr { + __u32 flags; __u64 vmxon_pa; __u64 vmcs12_pa; + __u64 preemption_timer_deadline; struct { __u16 flags; } smm; }; +struct kvm_svm_nested_state_data { + /* Save area only used if KVM_STATE_NESTED_RUN_PENDING. */ + __u8 vmcb12[KVM_STATE_NESTED_SVM_VMCB_SIZE]; +}; + +struct kvm_svm_nested_state_hdr { + __u64 vmcb_pa; +}; + /* for KVM_CAP_NESTED_STATE */ struct kvm_nested_state { __u16 flags; @@ -419,6 +435,7 @@ struct kvm_nested_state { union { struct kvm_vmx_nested_state_hdr vmx; + struct kvm_svm_nested_state_hdr svm; /* Pad the header to 128 bytes. */ __u8 pad[120]; @@ -431,6 +448,7 @@ struct kvm_nested_state { */ union { struct kvm_vmx_nested_state_data vmx[0]; + struct kvm_svm_nested_state_data svm[0]; } data; }; diff --git a/linux-headers/asm-x86/unistd.h b/linux-headers/asm-x86/unistd.h index 498d1515c6..d2af42d61d 100644 --- a/linux-headers/asm-x86/unistd.h +++ b/linux-headers/asm-x86/unistd.h @@ -2,8 +2,15 @@ #ifndef _ASM_X86_UNISTD_H #define _ASM_X86_UNISTD_H -/* x32 syscall flag bit */ -#define __X32_SYSCALL_BIT 0x40000000UL +/* + * x32 syscall flag bit. Some user programs expect syscall NR macros + * and __X32_SYSCALL_BIT to have type int, even though syscall numbers + * are, for practical purposes, unsigned long. + * + * Fortunately, expressions like (nr & ~__X32_SYSCALL_BIT) do the right + * thing regardless. + */ +#define __X32_SYSCALL_BIT 0x40000000 # ifdef __i386__ # include <asm/unistd_32.h> diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 1e6c1a5867..c727981d4a 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -428,6 +428,7 @@ #define __NR_clone3 435 #define __NR_openat2 437 #define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 #endif /* _ASM_X86_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index 6daf0aecb2..843fa62745 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -350,6 +350,7 @@ #define __NR_clone3 435 #define __NR_openat2 437 #define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 #endif /* _ASM_X86_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index e3f17ef370..7d63d703ca 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -303,6 +303,7 @@ #define __NR_clone3 (__X32_SYSCALL_BIT + 435) #define __NR_openat2 (__X32_SYSCALL_BIT + 437) #define __NR_pidfd_getfd (__X32_SYSCALL_BIT + 438) +#define __NR_faccessat2 (__X32_SYSCALL_BIT + 439) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 9804495a46..a28c366737 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -116,7 +116,7 @@ struct kvm_irq_level { * ACPI gsi notion of irq. * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47.. * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23.. - * For ARM: See Documentation/virt/kvm/api.txt + * For ARM: See Documentation/virt/kvm/api.rst */ union { __u32 irq; @@ -188,10 +188,13 @@ struct kvm_s390_cmma_log { struct kvm_hyperv_exit { #define KVM_EXIT_HYPERV_SYNIC 1 #define KVM_EXIT_HYPERV_HCALL 2 +#define KVM_EXIT_HYPERV_SYNDBG 3 __u32 type; + __u32 pad1; union { struct { __u32 msr; + __u32 pad2; __u64 control; __u64 evt_page; __u64 msg_page; @@ -201,6 +204,15 @@ struct kvm_hyperv_exit { __u64 result; __u64 params[2]; } hcall; + struct { + __u32 msr; + __u32 pad2; + __u64 control; + __u64 status; + __u64 send_page; + __u64 recv_page; + __u64 pending_page; + } syndbg; } u; }; @@ -1017,6 +1029,8 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_VCPU_RESETS 179 #define KVM_CAP_S390_PROTECTED 180 #define KVM_CAP_PPC_SECURE_GUEST 181 +#define KVM_CAP_HALT_POLL 182 +#define KVM_CAP_ASYNC_PF_INT 183 #ifdef KVM_CAP_IRQ_ROUTING @@ -1107,7 +1121,7 @@ struct kvm_xen_hvm_config { * * KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies * the irqfd to operate in resampling mode for level triggered interrupt - * emulation. See Documentation/virt/kvm/api.txt. + * emulation. See Documentation/virt/kvm/api.rst. */ #define KVM_IRQFD_FLAG_RESAMPLE (1 << 1) diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index 31f971e896..51d8b3940e 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -83,6 +83,8 @@ struct sev_user_data_status { __u32 guest_count; /* Out */ } __attribute__((packed)); +#define SEV_STATUS_FLAGS_CONFIG_ES 0x0100 + /** * struct sev_user_data_pek_csr - PEK_CSR command parameters * diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index a41c452865..f09df262c4 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -305,6 +305,7 @@ struct vfio_region_info_cap_type { #define VFIO_REGION_TYPE_PCI_VENDOR_MASK (0xffff) #define VFIO_REGION_TYPE_GFX (1) #define VFIO_REGION_TYPE_CCW (2) +#define VFIO_REGION_TYPE_MIGRATION (3) /* sub-types for VFIO_REGION_TYPE_PCI_* */ @@ -378,6 +379,235 @@ struct vfio_region_gfx_edid { /* sub-types for VFIO_REGION_TYPE_CCW */ #define VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD (1) +#define VFIO_REGION_SUBTYPE_CCW_SCHIB (2) +#define VFIO_REGION_SUBTYPE_CCW_CRW (3) + +/* sub-types for VFIO_REGION_TYPE_MIGRATION */ +#define VFIO_REGION_SUBTYPE_MIGRATION (1) + +/* + * The structure vfio_device_migration_info is placed at the 0th offset of + * the VFIO_REGION_SUBTYPE_MIGRATION region to get and set VFIO device related + * migration information. Field accesses from this structure are only supported + * at their native width and alignment. Otherwise, the result is undefined and + * vendor drivers should return an error. + * + * device_state: (read/write) + * - The user application writes to this field to inform the vendor driver + * about the device state to be transitioned to. + * - The vendor driver should take the necessary actions to change the + * device state. After successful transition to a given state, the + * vendor driver should return success on write(device_state, state) + * system call. If the device state transition fails, the vendor driver + * should return an appropriate -errno for the fault condition. + * - On the user application side, if the device state transition fails, + * that is, if write(device_state, state) returns an error, read + * device_state again to determine the current state of the device from + * the vendor driver. + * - The vendor driver should return previous state of the device unless + * the vendor driver has encountered an internal error, in which case + * the vendor driver may report the device_state VFIO_DEVICE_STATE_ERROR. + * - The user application must use the device reset ioctl to recover the + * device from VFIO_DEVICE_STATE_ERROR state. If the device is + * indicated to be in a valid device state by reading device_state, the + * user application may attempt to transition the device to any valid + * state reachable from the current state or terminate itself. + * + * device_state consists of 3 bits: + * - If bit 0 is set, it indicates the _RUNNING state. If bit 0 is clear, + * it indicates the _STOP state. When the device state is changed to + * _STOP, driver should stop the device before write() returns. + * - If bit 1 is set, it indicates the _SAVING state, which means that the + * driver should start gathering device state information that will be + * provided to the VFIO user application to save the device's state. + * - If bit 2 is set, it indicates the _RESUMING state, which means that + * the driver should prepare to resume the device. Data provided through + * the migration region should be used to resume the device. + * Bits 3 - 31 are reserved for future use. To preserve them, the user + * application should perform a read-modify-write operation on this + * field when modifying the specified bits. + * + * +------- _RESUMING + * |+------ _SAVING + * ||+----- _RUNNING + * ||| + * 000b => Device Stopped, not saving or resuming + * 001b => Device running, which is the default state + * 010b => Stop the device & save the device state, stop-and-copy state + * 011b => Device running and save the device state, pre-copy state + * 100b => Device stopped and the device state is resuming + * 101b => Invalid state + * 110b => Error state + * 111b => Invalid state + * + * State transitions: + * + * _RESUMING _RUNNING Pre-copy Stop-and-copy _STOP + * (100b) (001b) (011b) (010b) (000b) + * 0. Running or default state + * | + * + * 1. Normal Shutdown (optional) + * |------------------------------------->| + * + * 2. Save the state or suspend + * |------------------------->|---------->| + * + * 3. Save the state during live migration + * |----------->|------------>|---------->| + * + * 4. Resuming + * |<---------| + * + * 5. Resumed + * |--------->| + * + * 0. Default state of VFIO device is _RUNNNG when the user application starts. + * 1. During normal shutdown of the user application, the user application may + * optionally change the VFIO device state from _RUNNING to _STOP. This + * transition is optional. The vendor driver must support this transition but + * must not require it. + * 2. When the user application saves state or suspends the application, the + * device state transitions from _RUNNING to stop-and-copy and then to _STOP. + * On state transition from _RUNNING to stop-and-copy, driver must stop the + * device, save the device state and send it to the application through the + * migration region. The sequence to be followed for such transition is given + * below. + * 3. In live migration of user application, the state transitions from _RUNNING + * to pre-copy, to stop-and-copy, and to _STOP. + * On state transition from _RUNNING to pre-copy, the driver should start + * gathering the device state while the application is still running and send + * the device state data to application through the migration region. + * On state transition from pre-copy to stop-and-copy, the driver must stop + * the device, save the device state and send it to the user application + * through the migration region. + * Vendor drivers must support the pre-copy state even for implementations + * where no data is provided to the user before the stop-and-copy state. The + * user must not be required to consume all migration data before the device + * transitions to a new state, including the stop-and-copy state. + * The sequence to be followed for above two transitions is given below. + * 4. To start the resuming phase, the device state should be transitioned from + * the _RUNNING to the _RESUMING state. + * In the _RESUMING state, the driver should use the device state data + * received through the migration region to resume the device. + * 5. After providing saved device data to the driver, the application should + * change the state from _RESUMING to _RUNNING. + * + * reserved: + * Reads on this field return zero and writes are ignored. + * + * pending_bytes: (read only) + * The number of pending bytes still to be migrated from the vendor driver. + * + * data_offset: (read only) + * The user application should read data_offset field from the migration + * region. The user application should read the device data from this + * offset within the migration region during the _SAVING state or write + * the device data during the _RESUMING state. See below for details of + * sequence to be followed. + * + * data_size: (read/write) + * The user application should read data_size to get the size in bytes of + * the data copied in the migration region during the _SAVING state and + * write the size in bytes of the data copied in the migration region + * during the _RESUMING state. + * + * The format of the migration region is as follows: + * ------------------------------------------------------------------ + * |vfio_device_migration_info| data section | + * | | /////////////////////////////// | + * ------------------------------------------------------------------ + * ^ ^ + * offset 0-trapped part data_offset + * + * The structure vfio_device_migration_info is always followed by the data + * section in the region, so data_offset will always be nonzero. The offset + * from where the data is copied is decided by the kernel driver. The data + * section can be trapped, mmapped, or partitioned, depending on how the kernel + * driver defines the data section. The data section partition can be defined + * as mapped by the sparse mmap capability. If mmapped, data_offset must be + * page aligned, whereas initial section which contains the + * vfio_device_migration_info structure, might not end at the offset, which is + * page aligned. The user is not required to access through mmap regardless + * of the capabilities of the region mmap. + * The vendor driver should determine whether and how to partition the data + * section. The vendor driver should return data_offset accordingly. + * + * The sequence to be followed while in pre-copy state and stop-and-copy state + * is as follows: + * a. Read pending_bytes, indicating the start of a new iteration to get device + * data. Repeated read on pending_bytes at this stage should have no side + * effects. + * If pending_bytes == 0, the user application should not iterate to get data + * for that device. + * If pending_bytes > 0, perform the following steps. + * b. Read data_offset, indicating that the vendor driver should make data + * available through the data section. The vendor driver should return this + * read operation only after data is available from (region + data_offset) + * to (region + data_offset + data_size). + * c. Read data_size, which is the amount of data in bytes available through + * the migration region. + * Read on data_offset and data_size should return the offset and size of + * the current buffer if the user application reads data_offset and + * data_size more than once here. + * d. Read data_size bytes of data from (region + data_offset) from the + * migration region. + * e. Process the data. + * f. Read pending_bytes, which indicates that the data from the previous + * iteration has been read. If pending_bytes > 0, go to step b. + * + * The user application can transition from the _SAVING|_RUNNING + * (pre-copy state) to the _SAVING (stop-and-copy) state regardless of the + * number of pending bytes. The user application should iterate in _SAVING + * (stop-and-copy) until pending_bytes is 0. + * + * The sequence to be followed while _RESUMING device state is as follows: + * While data for this device is available, repeat the following steps: + * a. Read data_offset from where the user application should write data. + * b. Write migration data starting at the migration region + data_offset for + * the length determined by data_size from the migration source. + * c. Write data_size, which indicates to the vendor driver that data is + * written in the migration region. Vendor driver must return this write + * operations on consuming data. Vendor driver should apply the + * user-provided migration region data to the device resume state. + * + * If an error occurs during the above sequences, the vendor driver can return + * an error code for next read() or write() operation, which will terminate the + * loop. The user application should then take the next necessary action, for + * example, failing migration or terminating the user application. + * + * For the user application, data is opaque. The user application should write + * data in the same order as the data is received and the data should be of + * same transaction size at the source. + */ + +struct vfio_device_migration_info { + __u32 device_state; /* VFIO device state */ +#define VFIO_DEVICE_STATE_STOP (0) +#define VFIO_DEVICE_STATE_RUNNING (1 << 0) +#define VFIO_DEVICE_STATE_SAVING (1 << 1) +#define VFIO_DEVICE_STATE_RESUMING (1 << 2) +#define VFIO_DEVICE_STATE_MASK (VFIO_DEVICE_STATE_RUNNING | \ + VFIO_DEVICE_STATE_SAVING | \ + VFIO_DEVICE_STATE_RESUMING) + +#define VFIO_DEVICE_STATE_VALID(state) \ + (state & VFIO_DEVICE_STATE_RESUMING ? \ + (state & VFIO_DEVICE_STATE_MASK) == VFIO_DEVICE_STATE_RESUMING : 1) + +#define VFIO_DEVICE_STATE_IS_ERROR(state) \ + ((state & VFIO_DEVICE_STATE_MASK) == (VFIO_DEVICE_STATE_SAVING | \ + VFIO_DEVICE_STATE_RESUMING)) + +#define VFIO_DEVICE_STATE_SET_ERROR(state) \ + ((state & ~VFIO_DEVICE_STATE_MASK) | VFIO_DEVICE_SATE_SAVING | \ + VFIO_DEVICE_STATE_RESUMING) + + __u32 reserved; + __u64 pending_bytes; + __u64 data_offset; + __u64 data_size; +}; /* * The MSIX mappable capability informs that MSIX data of a BAR can be mmapped @@ -577,6 +807,7 @@ enum { enum { VFIO_CCW_IO_IRQ_INDEX, + VFIO_CCW_CRW_IRQ_INDEX, VFIO_CCW_NUM_IRQS }; @@ -785,6 +1016,29 @@ struct vfio_iommu_type1_info_cap_iova_range { struct vfio_iova_range iova_ranges[]; }; +/* + * The migration capability allows to report supported features for migration. + * + * The structures below define version 1 of this capability. + * + * The existence of this capability indicates that IOMMU kernel driver supports + * dirty page logging. + * + * pgsize_bitmap: Kernel driver returns bitmap of supported page sizes for dirty + * page logging. + * max_dirty_bitmap_size: Kernel driver returns maximum supported dirty bitmap + * size in bytes that can be used by user applications when getting the dirty + * bitmap. + */ +#define VFIO_IOMMU_TYPE1_INFO_CAP_MIGRATION 1 + +struct vfio_iommu_type1_info_cap_migration { + struct vfio_info_cap_header header; + __u32 flags; + __u64 pgsize_bitmap; + __u64 max_dirty_bitmap_size; /* in bytes */ +}; + #define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12) /** @@ -805,6 +1059,12 @@ struct vfio_iommu_type1_dma_map { #define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13) +struct vfio_bitmap { + __u64 pgsize; /* page size for bitmap in bytes */ + __u64 size; /* in bytes */ + __u64 *data; /* one bit per page */ +}; + /** * VFIO_IOMMU_UNMAP_DMA - _IOWR(VFIO_TYPE, VFIO_BASE + 14, * struct vfio_dma_unmap) @@ -814,12 +1074,23 @@ struct vfio_iommu_type1_dma_map { * field. No guarantee is made to the user that arbitrary unmaps of iova * or size different from those used in the original mapping call will * succeed. + * VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP should be set to get the dirty bitmap + * before unmapping IO virtual addresses. When this flag is set, the user must + * provide a struct vfio_bitmap in data[]. User must provide zero-allocated + * memory via vfio_bitmap.data and its size in the vfio_bitmap.size field. + * A bit in the bitmap represents one page, of user provided page size in + * vfio_bitmap.pgsize field, consecutively starting from iova offset. Bit set + * indicates that the page at that offset from iova is dirty. A Bitmap of the + * pages in the range of unmapped size is returned in the user-provided + * vfio_bitmap.data. */ struct vfio_iommu_type1_dma_unmap { __u32 argsz; __u32 flags; +#define VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP (1 << 0) __u64 iova; /* IO virtual address */ __u64 size; /* Size of mapping (bytes) */ + __u8 data[]; }; #define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14) @@ -831,6 +1102,57 @@ struct vfio_iommu_type1_dma_unmap { #define VFIO_IOMMU_ENABLE _IO(VFIO_TYPE, VFIO_BASE + 15) #define VFIO_IOMMU_DISABLE _IO(VFIO_TYPE, VFIO_BASE + 16) +/** + * VFIO_IOMMU_DIRTY_PAGES - _IOWR(VFIO_TYPE, VFIO_BASE + 17, + * struct vfio_iommu_type1_dirty_bitmap) + * IOCTL is used for dirty pages logging. + * Caller should set flag depending on which operation to perform, details as + * below: + * + * Calling the IOCTL with VFIO_IOMMU_DIRTY_PAGES_FLAG_START flag set, instructs + * the IOMMU driver to log pages that are dirtied or potentially dirtied by + * the device; designed to be used when a migration is in progress. Dirty pages + * are logged until logging is disabled by user application by calling the IOCTL + * with VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP flag. + * + * Calling the IOCTL with VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP flag set, instructs + * the IOMMU driver to stop logging dirtied pages. + * + * Calling the IOCTL with VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP flag set + * returns the dirty pages bitmap for IOMMU container for a given IOVA range. + * The user must specify the IOVA range and the pgsize through the structure + * vfio_iommu_type1_dirty_bitmap_get in the data[] portion. This interface + * supports getting a bitmap of the smallest supported pgsize only and can be + * modified in future to get a bitmap of any specified supported pgsize. The + * user must provide a zeroed memory area for the bitmap memory and specify its + * size in bitmap.size. One bit is used to represent one page consecutively + * starting from iova offset. The user should provide page size in bitmap.pgsize + * field. A bit set in the bitmap indicates that the page at that offset from + * iova is dirty. The caller must set argsz to a value including the size of + * structure vfio_iommu_type1_dirty_bitmap_get, but excluding the size of the + * actual bitmap. If dirty pages logging is not enabled, an error will be + * returned. + * + * Only one of the flags _START, _STOP and _GET may be specified at a time. + * + */ +struct vfio_iommu_type1_dirty_bitmap { + __u32 argsz; + __u32 flags; +#define VFIO_IOMMU_DIRTY_PAGES_FLAG_START (1 << 0) +#define VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP (1 << 1) +#define VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP (1 << 2) + __u8 data[]; +}; + +struct vfio_iommu_type1_dirty_bitmap_get { + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of iova range */ + struct vfio_bitmap bitmap; +}; + +#define VFIO_IOMMU_DIRTY_PAGES _IO(VFIO_TYPE, VFIO_BASE + 17) + /* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */ /* diff --git a/linux-headers/linux/vfio_ccw.h b/linux-headers/linux/vfio_ccw.h index fcc3e69ef5..516496f1d4 100644 --- a/linux-headers/linux/vfio_ccw.h +++ b/linux-headers/linux/vfio_ccw.h @@ -34,4 +34,23 @@ struct ccw_cmd_region { __u32 ret_code; } __attribute__((packed)); +/* + * Used for processing commands that read the subchannel-information block + * Reading this region triggers a stsch() to hardware + * Note: this is controlled by a capability + */ +struct ccw_schib_region { +#define SCHIB_AREA_SIZE 52 + __u8 schib_area[SCHIB_AREA_SIZE]; +} __attribute__((packed)); + +/* + * Used for returning a Channel Report Word to userspace. + * Note: this is controlled by a capability + */ +struct ccw_crw_region { + __u32 crw; + __u32 pad; +} __attribute__((packed)); + #endif diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index 9fe72e4b13..0c2349612e 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -15,6 +15,8 @@ #include <linux/types.h> #include <linux/ioctl.h> +#define VHOST_FILE_UNBIND -1 + /* ioctls */ #define VHOST_VIRTIO 0xAF @@ -140,4 +142,6 @@ /* Get the max ring size. */ #define VHOST_VDPA_GET_VRING_NUM _IOR(VHOST_VIRTIO, 0x76, __u16) +/* Set event fd for config interrupt*/ +#define VHOST_VDPA_SET_CONFIG_CALL _IOW(VHOST_VIRTIO, 0x77, int) #endif diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 5b7a36b5d7..e44e18062c 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1108,7 +1108,7 @@ static void arm_set_pmu(Object *obj, bool value, Error **errp) ARMCPU *cpu = ARM_CPU(obj); if (value) { - if (kvm_enabled() && !kvm_arm_pmu_supported(CPU(cpu))) { + if (kvm_enabled() && !kvm_arm_pmu_supported()) { error_setg(errp, "'pmu' feature not supported by KVM on this host"); return; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 677584e5da..cf66b8c7fb 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2334,7 +2334,7 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) * migration or KVM state synchronization. (Typically this is for "registers" * which are actually used as instructions for cache maintenance and so on.) * IO indicates that this register does I/O and therefore its accesses - * need to be surrounded by gen_io_start()/gen_io_end(). In particular, + * need to be marked with gen_io_start() and also end the TB. In particular, * registers which implement clocks or timers require this. * RAISES_EXC is for when the read or write hook might raise an exception; * the generated code will synchronize the CPU state before calling the hook diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 778cecc2e6..a0c1d8894b 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -266,7 +266,7 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) /* Collect the set of vector lengths supported by KVM. */ bitmap_zero(kvm_supported, ARM_MAX_VQ); - if (kvm_enabled() && kvm_arm_sve_supported(CPU(cpu))) { + if (kvm_enabled() && kvm_arm_sve_supported()) { kvm_arm_sve_get_vls(CPU(cpu), kvm_supported); } else if (kvm_enabled()) { assert(!cpu_isar_feature(aa64_sve, cpu)); @@ -473,7 +473,7 @@ static void cpu_max_set_sve_max_vq(Object *obj, Visitor *v, const char *name, return; } - if (kvm_enabled() && !kvm_arm_sve_supported(CPU(cpu))) { + if (kvm_enabled() && !kvm_arm_sve_supported()) { error_setg(errp, "cannot set sve-max-vq"); error_append_hint(errp, "SVE not supported by KVM on this host\n"); return; @@ -519,7 +519,7 @@ static void cpu_arm_set_sve_vq(Object *obj, Visitor *v, const char *name, return; } - if (value && kvm_enabled() && !kvm_arm_sve_supported(CPU(cpu))) { + if (value && kvm_enabled() && !kvm_arm_sve_supported()) { error_setg(errp, "cannot enable %s", name); error_append_hint(errp, "SVE not supported by KVM on this host\n"); return; @@ -556,7 +556,7 @@ static void cpu_arm_set_sve(Object *obj, Visitor *v, const char *name, return; } - if (value && kvm_enabled() && !kvm_arm_sve_supported(CPU(cpu))) { + if (value && kvm_enabled() && !kvm_arm_sve_supported()) { error_setg(errp, "'sve' feature not supported by KVM on this host"); return; } @@ -751,7 +751,7 @@ static void aarch64_cpu_set_aarch64(Object *obj, bool value, Error **errp) * uniform execution state like do_interrupt. */ if (value == false) { - if (!kvm_enabled() || !kvm_arm_aarch32_supported(CPU(cpu))) { + if (!kvm_enabled() || !kvm_arm_aarch32_supported()) { error_setg(errp, "'aarch64' feature cannot be disabled " "unless KVM is enabled and 32-bit EL1 " "is supported"); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index eef3bbd1cc..7c672c78b8 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -208,9 +208,9 @@ void kvm_arm_add_vcpu_properties(Object *obj) } } -bool kvm_arm_pmu_supported(CPUState *cpu) +bool kvm_arm_pmu_supported(void) { - return kvm_check_extension(cpu->kvm_state, KVM_CAP_ARM_PMU_V3); + return kvm_check_extension(kvm_state, KVM_CAP_ARM_PMU_V3); } int kvm_arm_get_max_vm_ipa_size(MachineState *ms) diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index f09ed9f4df..3dc494aaa7 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -652,18 +652,14 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) return true; } -bool kvm_arm_aarch32_supported(CPUState *cpu) +bool kvm_arm_aarch32_supported(void) { - KVMState *s = KVM_STATE(current_accel()); - - return kvm_check_extension(s, KVM_CAP_ARM_EL1_32BIT); + return kvm_check_extension(kvm_state, KVM_CAP_ARM_EL1_32BIT); } -bool kvm_arm_sve_supported(CPUState *cpu) +bool kvm_arm_sve_supported(void) { - KVMState *s = KVM_STATE(current_accel()); - - return kvm_check_extension(s, KVM_CAP_ARM_SVE); + return kvm_check_extension(kvm_state, KVM_CAP_ARM_SVE); } QEMU_BUILD_BUG_ON(KVM_ARM64_SVE_VQ_MIN != 1); @@ -798,7 +794,7 @@ int kvm_arch_init_vcpu(CPUState *cs) env->features &= ~(1ULL << ARM_FEATURE_PMU); } if (cpu_isar_feature(aa64_sve, cpu)) { - assert(kvm_arm_sve_supported(cs)); + assert(kvm_arm_sve_supported()); cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_SVE; } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 48bf5e16d5..a4ce4fd93d 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -269,29 +269,26 @@ void kvm_arm_add_vcpu_properties(Object *obj); /** * kvm_arm_aarch32_supported: - * @cs: CPUState * - * Returns: true if the KVM VCPU can enable AArch32 mode + * Returns: true if KVM can enable AArch32 mode * and false otherwise. */ -bool kvm_arm_aarch32_supported(CPUState *cs); +bool kvm_arm_aarch32_supported(void); /** * kvm_arm_pmu_supported: - * @cs: CPUState * - * Returns: true if the KVM VCPU can enable its PMU + * Returns: true if KVM can enable the PMU * and false otherwise. */ -bool kvm_arm_pmu_supported(CPUState *cs); +bool kvm_arm_pmu_supported(void); /** * kvm_arm_sve_supported: - * @cs: CPUState * - * Returns true if the KVM VCPU can enable SVE and false otherwise. + * Returns true if KVM can enable SVE and false otherwise. */ -bool kvm_arm_sve_supported(CPUState *cs); +bool kvm_arm_sve_supported(void); /** * kvm_arm_get_max_vm_ipa_size: @@ -359,17 +356,17 @@ static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) static inline void kvm_arm_add_vcpu_properties(Object *obj) {} -static inline bool kvm_arm_aarch32_supported(CPUState *cs) +static inline bool kvm_arm_aarch32_supported(void) { return false; } -static inline bool kvm_arm_pmu_supported(CPUState *cs) +static inline bool kvm_arm_pmu_supported(void) { return false; } -static inline bool kvm_arm_sve_supported(CPUState *cs) +static inline bool kvm_arm_sve_supported(void) { return false; } diff --git a/target/arm/neon-dp.decode b/target/arm/neon-dp.decode index 6d890b2161..686f9fbf46 100644 --- a/target/arm/neon-dp.decode +++ b/target/arm/neon-dp.decode @@ -429,6 +429,112 @@ Vimm_1r 1111 001 . 1 . 000 ... .... cmode:4 0 . op:1 1 .... @1reg_imm vm=%vm_dp vd=%vd_dp size=1 VDUP_scalar 1111 001 1 1 . 11 index:1 100 .... 11 000 q:1 . 0 .... \ vm=%vm_dp vd=%vd_dp size=2 + + ################################################################## + # 2-reg-misc grouping: + # 1111 001 11 D 11 size:2 opc1:2 Vd:4 0 opc2:4 q:1 M 0 Vm:4 + ################################################################## + + &2misc vd vm q size + + @2misc .... ... .. . .. size:2 .. .... . .... q:1 . . .... \ + &2misc vm=%vm_dp vd=%vd_dp + @2misc_q0 .... ... .. . .. size:2 .. .... . .... . . . .... \ + &2misc vm=%vm_dp vd=%vd_dp q=0 + @2misc_q1 .... ... .. . .. size:2 .. .... . .... . . . .... \ + &2misc vm=%vm_dp vd=%vd_dp q=1 + + VREV64 1111 001 11 . 11 .. 00 .... 0 0000 . . 0 .... @2misc + VREV32 1111 001 11 . 11 .. 00 .... 0 0001 . . 0 .... @2misc + VREV16 1111 001 11 . 11 .. 00 .... 0 0010 . . 0 .... @2misc + + VPADDL_S 1111 001 11 . 11 .. 00 .... 0 0100 . . 0 .... @2misc + VPADDL_U 1111 001 11 . 11 .. 00 .... 0 0101 . . 0 .... @2misc + + AESE 1111 001 11 . 11 .. 00 .... 0 0110 0 . 0 .... @2misc_q1 + AESD 1111 001 11 . 11 .. 00 .... 0 0110 1 . 0 .... @2misc_q1 + AESMC 1111 001 11 . 11 .. 00 .... 0 0111 0 . 0 .... @2misc_q1 + AESIMC 1111 001 11 . 11 .. 00 .... 0 0111 1 . 0 .... @2misc_q1 + + VCLS 1111 001 11 . 11 .. 00 .... 0 1000 . . 0 .... @2misc + VCLZ 1111 001 11 . 11 .. 00 .... 0 1001 . . 0 .... @2misc + VCNT 1111 001 11 . 11 .. 00 .... 0 1010 . . 0 .... @2misc + + VMVN 1111 001 11 . 11 .. 00 .... 0 1011 . . 0 .... @2misc + + VPADAL_S 1111 001 11 . 11 .. 00 .... 0 1100 . . 0 .... @2misc + VPADAL_U 1111 001 11 . 11 .. 00 .... 0 1101 . . 0 .... @2misc + + VQABS 1111 001 11 . 11 .. 00 .... 0 1110 . . 0 .... @2misc + VQNEG 1111 001 11 . 11 .. 00 .... 0 1111 . . 0 .... @2misc + + VCGT0 1111 001 11 . 11 .. 01 .... 0 0000 . . 0 .... @2misc + VCGE0 1111 001 11 . 11 .. 01 .... 0 0001 . . 0 .... @2misc + VCEQ0 1111 001 11 . 11 .. 01 .... 0 0010 . . 0 .... @2misc + VCLE0 1111 001 11 . 11 .. 01 .... 0 0011 . . 0 .... @2misc + VCLT0 1111 001 11 . 11 .. 01 .... 0 0100 . . 0 .... @2misc + + SHA1H 1111 001 11 . 11 .. 01 .... 0 0101 1 . 0 .... @2misc_q1 + + VABS 1111 001 11 . 11 .. 01 .... 0 0110 . . 0 .... @2misc + VNEG 1111 001 11 . 11 .. 01 .... 0 0111 . . 0 .... @2misc + + VCGT0_F 1111 001 11 . 11 .. 01 .... 0 1000 . . 0 .... @2misc + VCGE0_F 1111 001 11 . 11 .. 01 .... 0 1001 . . 0 .... @2misc + VCEQ0_F 1111 001 11 . 11 .. 01 .... 0 1010 . . 0 .... @2misc + VCLE0_F 1111 001 11 . 11 .. 01 .... 0 1011 . . 0 .... @2misc + VCLT0_F 1111 001 11 . 11 .. 01 .... 0 1100 . . 0 .... @2misc + + VABS_F 1111 001 11 . 11 .. 01 .... 0 1110 . . 0 .... @2misc + VNEG_F 1111 001 11 . 11 .. 01 .... 0 1111 . . 0 .... @2misc + + VSWP 1111 001 11 . 11 .. 10 .... 0 0000 . . 0 .... @2misc + VTRN 1111 001 11 . 11 .. 10 .... 0 0001 . . 0 .... @2misc + VUZP 1111 001 11 . 11 .. 10 .... 0 0010 . . 0 .... @2misc + VZIP 1111 001 11 . 11 .. 10 .... 0 0011 . . 0 .... @2misc + + VMOVN 1111 001 11 . 11 .. 10 .... 0 0100 0 . 0 .... @2misc_q0 + # VQMOVUN: unsigned result (source is always signed) + VQMOVUN 1111 001 11 . 11 .. 10 .... 0 0100 1 . 0 .... @2misc_q0 + # VQMOVN: signed result, source may be signed (_S) or unsigned (_U) + VQMOVN_S 1111 001 11 . 11 .. 10 .... 0 0101 0 . 0 .... @2misc_q0 + VQMOVN_U 1111 001 11 . 11 .. 10 .... 0 0101 1 . 0 .... @2misc_q0 + + VSHLL 1111 001 11 . 11 .. 10 .... 0 0110 0 . 0 .... @2misc_q0 + + SHA1SU1 1111 001 11 . 11 .. 10 .... 0 0111 0 . 0 .... @2misc_q1 + SHA256SU0 1111 001 11 . 11 .. 10 .... 0 0111 1 . 0 .... @2misc_q1 + + VRINTN 1111 001 11 . 11 .. 10 .... 0 1000 . . 0 .... @2misc + VRINTX 1111 001 11 . 11 .. 10 .... 0 1001 . . 0 .... @2misc + VRINTA 1111 001 11 . 11 .. 10 .... 0 1010 . . 0 .... @2misc + VRINTZ 1111 001 11 . 11 .. 10 .... 0 1011 . . 0 .... @2misc + + VCVT_F16_F32 1111 001 11 . 11 .. 10 .... 0 1100 0 . 0 .... @2misc_q0 + + VRINTM 1111 001 11 . 11 .. 10 .... 0 1101 . . 0 .... @2misc + + VCVT_F32_F16 1111 001 11 . 11 .. 10 .... 0 1110 0 . 0 .... @2misc_q0 + + VRINTP 1111 001 11 . 11 .. 10 .... 0 1111 . . 0 .... @2misc + + VCVTAS 1111 001 11 . 11 .. 11 .... 0 0000 . . 0 .... @2misc + VCVTAU 1111 001 11 . 11 .. 11 .... 0 0001 . . 0 .... @2misc + VCVTNS 1111 001 11 . 11 .. 11 .... 0 0010 . . 0 .... @2misc + VCVTNU 1111 001 11 . 11 .. 11 .... 0 0011 . . 0 .... @2misc + VCVTPS 1111 001 11 . 11 .. 11 .... 0 0100 . . 0 .... @2misc + VCVTPU 1111 001 11 . 11 .. 11 .... 0 0101 . . 0 .... @2misc + VCVTMS 1111 001 11 . 11 .. 11 .... 0 0110 . . 0 .... @2misc + VCVTMU 1111 001 11 . 11 .. 11 .... 0 0111 . . 0 .... @2misc + + VRECPE 1111 001 11 . 11 .. 11 .... 0 1000 . . 0 .... @2misc + VRSQRTE 1111 001 11 . 11 .. 11 .... 0 1001 . . 0 .... @2misc + VRECPE_F 1111 001 11 . 11 .. 11 .... 0 1010 . . 0 .... @2misc + VRSQRTE_F 1111 001 11 . 11 .. 11 .... 0 1011 . . 0 .... @2misc + VCVT_FS 1111 001 11 . 11 .. 11 .... 0 1100 . . 0 .... @2misc + VCVT_FU 1111 001 11 . 11 .. 11 .... 0 1101 . . 0 .... @2misc + VCVT_SF 1111 001 11 . 11 .. 11 .... 0 1110 . . 0 .... @2misc + VCVT_UF 1111 001 11 . 11 .. 11 .... 0 1111 . . 0 .... @2misc ] # Subgroup for size != 0b11 diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index a0e72ad694..4cef862c41 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -9534,7 +9534,7 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, TCGv_i64 tcg_op = tcg_temp_new_i64(); TCGv_i64 tcg_zero = tcg_const_i64(0); TCGv_i64 tcg_res = tcg_temp_new_i64(); - NeonGenTwoDoubleOPFn *genfn; + NeonGenTwoDoubleOpFn *genfn; bool swap = false; int pass; @@ -9576,7 +9576,7 @@ static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, TCGv_i32 tcg_op = tcg_temp_new_i32(); TCGv_i32 tcg_zero = tcg_const_i32(0); TCGv_i32 tcg_res = tcg_temp_new_i32(); - NeonGenTwoSingleOPFn *genfn; + NeonGenTwoSingleOpFn *genfn; bool swap = false; int pass, maxpasses; @@ -11370,18 +11370,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genfn(tcg_res, tcg_op1, tcg_op2); } - if (opcode == 0xf) { - /* SABA, UABA: accumulating ops */ - static NeonGenTwoOpFn * const fns[3] = { - gen_helper_neon_add_u8, - gen_helper_neon_add_u16, - tcg_gen_add_i32, - }; - - read_vec_element_i32(s, tcg_op1, rd, pass, MO_32); - fns[size](tcg_res, tcg_op1, tcg_res); - } - write_vec_element_i32(s, tcg_res, rd, pass, MO_32); tcg_temp_free_i32(tcg_res); @@ -11917,8 +11905,8 @@ static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, } else { for (pass = 0; pass < maxpass; pass++) { TCGv_i64 tcg_op = tcg_temp_new_i64(); - NeonGenOneOpFn *genfn; - static NeonGenOneOpFn * const fns[2][2] = { + NeonGenOne64OpFn *genfn; + static NeonGenOne64OpFn * const fns[2][2] = { { gen_helper_neon_addlp_s8, gen_helper_neon_addlp_u8 }, { gen_helper_neon_addlp_s16, gen_helper_neon_addlp_u16 }, }; diff --git a/target/arm/translate-neon.inc.c b/target/arm/translate-neon.inc.c index a5aa56bbde..f6cb921573 100644 --- a/target/arm/translate-neon.inc.c +++ b/target/arm/translate-neon.inc.c @@ -54,6 +54,107 @@ static inline int rsub_8(DisasContext *s, int x) #include "decode-neon-ls.inc.c" #include "decode-neon-shared.inc.c" +/* Return the offset of a 2**SIZE piece of a NEON register, at index ELE, + * where 0 is the least significant end of the register. + */ +static inline long +neon_element_offset(int reg, int element, MemOp size) +{ + int element_size = 1 << size; + int ofs = element * element_size; +#ifdef HOST_WORDS_BIGENDIAN + /* Calculate the offset assuming fully little-endian, + * then XOR to account for the order of the 8-byte units. + */ + if (element_size < 8) { + ofs ^= 8 - element_size; + } +#endif + return neon_reg_offset(reg, 0) + ofs; +} + +static void neon_load_element(TCGv_i32 var, int reg, int ele, MemOp mop) +{ + long offset = neon_element_offset(reg, ele, mop & MO_SIZE); + + switch (mop) { + case MO_UB: + tcg_gen_ld8u_i32(var, cpu_env, offset); + break; + case MO_UW: + tcg_gen_ld16u_i32(var, cpu_env, offset); + break; + case MO_UL: + tcg_gen_ld_i32(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } +} + +static void neon_load_element64(TCGv_i64 var, int reg, int ele, MemOp mop) +{ + long offset = neon_element_offset(reg, ele, mop & MO_SIZE); + + switch (mop) { + case MO_UB: + tcg_gen_ld8u_i64(var, cpu_env, offset); + break; + case MO_UW: + tcg_gen_ld16u_i64(var, cpu_env, offset); + break; + case MO_UL: + tcg_gen_ld32u_i64(var, cpu_env, offset); + break; + case MO_Q: + tcg_gen_ld_i64(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } +} + +static void neon_store_element(int reg, int ele, MemOp size, TCGv_i32 var) +{ + long offset = neon_element_offset(reg, ele, size); + + switch (size) { + case MO_8: + tcg_gen_st8_i32(var, cpu_env, offset); + break; + case MO_16: + tcg_gen_st16_i32(var, cpu_env, offset); + break; + case MO_32: + tcg_gen_st_i32(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } +} + +static void neon_store_element64(int reg, int ele, MemOp size, TCGv_i64 var) +{ + long offset = neon_element_offset(reg, ele, size); + + switch (size) { + case MO_8: + tcg_gen_st8_i64(var, cpu_env, offset); + break; + case MO_16: + tcg_gen_st16_i64(var, cpu_env, offset); + break; + case MO_32: + tcg_gen_st32_i64(var, cpu_env, offset); + break; + case MO_64: + tcg_gen_st_i64(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } +} + static bool trans_VCMLA(DisasContext *s, arg_VCMLA *a) { int opr_sz; @@ -1664,7 +1765,7 @@ static bool trans_VSHLL_U_2sh(DisasContext *s, arg_2reg_shift *a) } static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a, - NeonGenTwoSingleOPFn *fn) + NeonGenTwoSingleOpFn *fn) { /* FP operations in 2-reg-and-shift group */ TCGv_i32 tmp, shiftv; @@ -2970,3 +3071,1091 @@ static bool trans_VDUP_scalar(DisasContext *s, arg_VDUP_scalar *a) a->q ? 16 : 8, a->q ? 16 : 8); return true; } + +static bool trans_VREV64(DisasContext *s, arg_VREV64 *a) +{ + int pass, half; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (a->size == 3) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + for (pass = 0; pass < (a->q ? 2 : 1); pass++) { + TCGv_i32 tmp[2]; + + for (half = 0; half < 2; half++) { + tmp[half] = neon_load_reg(a->vm, pass * 2 + half); + switch (a->size) { + case 0: + tcg_gen_bswap32_i32(tmp[half], tmp[half]); + break; + case 1: + gen_swap_half(tmp[half], tmp[half]); + break; + case 2: + break; + default: + g_assert_not_reached(); + } + } + neon_store_reg(a->vd, pass * 2, tmp[1]); + neon_store_reg(a->vd, pass * 2 + 1, tmp[0]); + } + return true; +} + +static bool do_2misc_pairwise(DisasContext *s, arg_2misc *a, + NeonGenWidenFn *widenfn, + NeonGenTwo64OpFn *opfn, + NeonGenTwo64OpFn *accfn) +{ + /* + * Pairwise long operations: widen both halves of the pair, + * combine the pairs with the opfn, and then possibly accumulate + * into the destination with the accfn. + */ + int pass; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (!widenfn) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + for (pass = 0; pass < a->q + 1; pass++) { + TCGv_i32 tmp; + TCGv_i64 rm0_64, rm1_64, rd_64; + + rm0_64 = tcg_temp_new_i64(); + rm1_64 = tcg_temp_new_i64(); + rd_64 = tcg_temp_new_i64(); + tmp = neon_load_reg(a->vm, pass * 2); + widenfn(rm0_64, tmp); + tcg_temp_free_i32(tmp); + tmp = neon_load_reg(a->vm, pass * 2 + 1); + widenfn(rm1_64, tmp); + tcg_temp_free_i32(tmp); + opfn(rd_64, rm0_64, rm1_64); + tcg_temp_free_i64(rm0_64); + tcg_temp_free_i64(rm1_64); + + if (accfn) { + TCGv_i64 tmp64 = tcg_temp_new_i64(); + neon_load_reg64(tmp64, a->vd + pass); + accfn(rd_64, tmp64, rd_64); + tcg_temp_free_i64(tmp64); + } + neon_store_reg64(rd_64, a->vd + pass); + tcg_temp_free_i64(rd_64); + } + return true; +} + +static bool trans_VPADDL_S(DisasContext *s, arg_2misc *a) +{ + static NeonGenWidenFn * const widenfn[] = { + gen_helper_neon_widen_s8, + gen_helper_neon_widen_s16, + tcg_gen_ext_i32_i64, + NULL, + }; + static NeonGenTwo64OpFn * const opfn[] = { + gen_helper_neon_paddl_u16, + gen_helper_neon_paddl_u32, + tcg_gen_add_i64, + NULL, + }; + + return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], NULL); +} + +static bool trans_VPADDL_U(DisasContext *s, arg_2misc *a) +{ + static NeonGenWidenFn * const widenfn[] = { + gen_helper_neon_widen_u8, + gen_helper_neon_widen_u16, + tcg_gen_extu_i32_i64, + NULL, + }; + static NeonGenTwo64OpFn * const opfn[] = { + gen_helper_neon_paddl_u16, + gen_helper_neon_paddl_u32, + tcg_gen_add_i64, + NULL, + }; + + return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], NULL); +} + +static bool trans_VPADAL_S(DisasContext *s, arg_2misc *a) +{ + static NeonGenWidenFn * const widenfn[] = { + gen_helper_neon_widen_s8, + gen_helper_neon_widen_s16, + tcg_gen_ext_i32_i64, + NULL, + }; + static NeonGenTwo64OpFn * const opfn[] = { + gen_helper_neon_paddl_u16, + gen_helper_neon_paddl_u32, + tcg_gen_add_i64, + NULL, + }; + static NeonGenTwo64OpFn * const accfn[] = { + gen_helper_neon_addl_u16, + gen_helper_neon_addl_u32, + tcg_gen_add_i64, + NULL, + }; + + return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], + accfn[a->size]); +} + +static bool trans_VPADAL_U(DisasContext *s, arg_2misc *a) +{ + static NeonGenWidenFn * const widenfn[] = { + gen_helper_neon_widen_u8, + gen_helper_neon_widen_u16, + tcg_gen_extu_i32_i64, + NULL, + }; + static NeonGenTwo64OpFn * const opfn[] = { + gen_helper_neon_paddl_u16, + gen_helper_neon_paddl_u32, + tcg_gen_add_i64, + NULL, + }; + static NeonGenTwo64OpFn * const accfn[] = { + gen_helper_neon_addl_u16, + gen_helper_neon_addl_u32, + tcg_gen_add_i64, + NULL, + }; + + return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], + accfn[a->size]); +} + +typedef void ZipFn(TCGv_ptr, TCGv_ptr); + +static bool do_zip_uzp(DisasContext *s, arg_2misc *a, + ZipFn *fn) +{ + TCGv_ptr pd, pm; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (!fn) { + /* Bad size or size/q combination */ + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + pd = vfp_reg_ptr(true, a->vd); + pm = vfp_reg_ptr(true, a->vm); + fn(pd, pm); + tcg_temp_free_ptr(pd); + tcg_temp_free_ptr(pm); + return true; +} + +static bool trans_VUZP(DisasContext *s, arg_2misc *a) +{ + static ZipFn * const fn[2][4] = { + { + gen_helper_neon_unzip8, + gen_helper_neon_unzip16, + NULL, + NULL, + }, { + gen_helper_neon_qunzip8, + gen_helper_neon_qunzip16, + gen_helper_neon_qunzip32, + NULL, + } + }; + return do_zip_uzp(s, a, fn[a->q][a->size]); +} + +static bool trans_VZIP(DisasContext *s, arg_2misc *a) +{ + static ZipFn * const fn[2][4] = { + { + gen_helper_neon_zip8, + gen_helper_neon_zip16, + NULL, + NULL, + }, { + gen_helper_neon_qzip8, + gen_helper_neon_qzip16, + gen_helper_neon_qzip32, + NULL, + } + }; + return do_zip_uzp(s, a, fn[a->q][a->size]); +} + +static bool do_vmovn(DisasContext *s, arg_2misc *a, + NeonGenNarrowEnvFn *narrowfn) +{ + TCGv_i64 rm; + TCGv_i32 rd0, rd1; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if (a->vm & 1) { + return false; + } + + if (!narrowfn) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + rm = tcg_temp_new_i64(); + rd0 = tcg_temp_new_i32(); + rd1 = tcg_temp_new_i32(); + + neon_load_reg64(rm, a->vm); + narrowfn(rd0, cpu_env, rm); + neon_load_reg64(rm, a->vm + 1); + narrowfn(rd1, cpu_env, rm); + neon_store_reg(a->vd, 0, rd0); + neon_store_reg(a->vd, 1, rd1); + tcg_temp_free_i64(rm); + return true; +} + +#define DO_VMOVN(INSN, FUNC) \ + static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ + { \ + static NeonGenNarrowEnvFn * const narrowfn[] = { \ + FUNC##8, \ + FUNC##16, \ + FUNC##32, \ + NULL, \ + }; \ + return do_vmovn(s, a, narrowfn[a->size]); \ + } + +DO_VMOVN(VMOVN, gen_neon_narrow_u) +DO_VMOVN(VQMOVUN, gen_helper_neon_unarrow_sat) +DO_VMOVN(VQMOVN_S, gen_helper_neon_narrow_sat_s) +DO_VMOVN(VQMOVN_U, gen_helper_neon_narrow_sat_u) + +static bool trans_VSHLL(DisasContext *s, arg_2misc *a) +{ + TCGv_i32 rm0, rm1; + TCGv_i64 rd; + static NeonGenWidenFn * const widenfns[] = { + gen_helper_neon_widen_u8, + gen_helper_neon_widen_u16, + tcg_gen_extu_i32_i64, + NULL, + }; + NeonGenWidenFn *widenfn = widenfns[a->size]; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if (a->vd & 1) { + return false; + } + + if (!widenfn) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + rd = tcg_temp_new_i64(); + + rm0 = neon_load_reg(a->vm, 0); + rm1 = neon_load_reg(a->vm, 1); + + widenfn(rd, rm0); + tcg_gen_shli_i64(rd, rd, 8 << a->size); + neon_store_reg64(rd, a->vd); + widenfn(rd, rm1); + tcg_gen_shli_i64(rd, rd, 8 << a->size); + neon_store_reg64(rd, a->vd + 1); + + tcg_temp_free_i64(rd); + tcg_temp_free_i32(rm0); + tcg_temp_free_i32(rm1); + return true; +} + +static bool trans_VCVT_F16_F32(DisasContext *s, arg_2misc *a) +{ + TCGv_ptr fpst; + TCGv_i32 ahp, tmp, tmp2, tmp3; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON) || + !dc_isar_feature(aa32_fp16_spconv, s)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if ((a->vm & 1) || (a->size != 1)) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + fpst = get_fpstatus_ptr(true); + ahp = get_ahp_flag(); + tmp = neon_load_reg(a->vm, 0); + gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp); + tmp2 = neon_load_reg(a->vm, 1); + gen_helper_vfp_fcvt_f32_to_f16(tmp2, tmp2, fpst, ahp); + tcg_gen_shli_i32(tmp2, tmp2, 16); + tcg_gen_or_i32(tmp2, tmp2, tmp); + tcg_temp_free_i32(tmp); + tmp = neon_load_reg(a->vm, 2); + gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp); + tmp3 = neon_load_reg(a->vm, 3); + neon_store_reg(a->vd, 0, tmp2); + gen_helper_vfp_fcvt_f32_to_f16(tmp3, tmp3, fpst, ahp); + tcg_gen_shli_i32(tmp3, tmp3, 16); + tcg_gen_or_i32(tmp3, tmp3, tmp); + neon_store_reg(a->vd, 1, tmp3); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); + + return true; +} + +static bool trans_VCVT_F32_F16(DisasContext *s, arg_2misc *a) +{ + TCGv_ptr fpst; + TCGv_i32 ahp, tmp, tmp2, tmp3; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON) || + !dc_isar_feature(aa32_fp16_spconv, s)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if ((a->vd & 1) || (a->size != 1)) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + fpst = get_fpstatus_ptr(true); + ahp = get_ahp_flag(); + tmp3 = tcg_temp_new_i32(); + tmp = neon_load_reg(a->vm, 0); + tmp2 = neon_load_reg(a->vm, 1); + tcg_gen_ext16u_i32(tmp3, tmp); + gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp); + neon_store_reg(a->vd, 0, tmp3); + tcg_gen_shri_i32(tmp, tmp, 16); + gen_helper_vfp_fcvt_f16_to_f32(tmp, tmp, fpst, ahp); + neon_store_reg(a->vd, 1, tmp); + tmp3 = tcg_temp_new_i32(); + tcg_gen_ext16u_i32(tmp3, tmp2); + gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp); + neon_store_reg(a->vd, 2, tmp3); + tcg_gen_shri_i32(tmp2, tmp2, 16); + gen_helper_vfp_fcvt_f16_to_f32(tmp2, tmp2, fpst, ahp); + neon_store_reg(a->vd, 3, tmp2); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); + + return true; +} + +static bool do_2misc_vec(DisasContext *s, arg_2misc *a, GVecGen2Fn *fn) +{ + int vec_size = a->q ? 16 : 8; + int rd_ofs = neon_reg_offset(a->vd, 0); + int rm_ofs = neon_reg_offset(a->vm, 0); + + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if (a->size == 3) { + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + fn(a->size, rd_ofs, rm_ofs, vec_size, vec_size); + + return true; +} + +#define DO_2MISC_VEC(INSN, FN) \ + static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ + { \ + return do_2misc_vec(s, a, FN); \ + } + +DO_2MISC_VEC(VNEG, tcg_gen_gvec_neg) +DO_2MISC_VEC(VABS, tcg_gen_gvec_abs) +DO_2MISC_VEC(VCEQ0, gen_gvec_ceq0) +DO_2MISC_VEC(VCGT0, gen_gvec_cgt0) +DO_2MISC_VEC(VCLE0, gen_gvec_cle0) +DO_2MISC_VEC(VCGE0, gen_gvec_cge0) +DO_2MISC_VEC(VCLT0, gen_gvec_clt0) + +static bool trans_VMVN(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0) { + return false; + } + return do_2misc_vec(s, a, tcg_gen_gvec_not); +} + +#define WRAP_2M_3_OOL_FN(WRAPNAME, FUNC, DATA) \ + static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \ + uint32_t rm_ofs, uint32_t oprsz, \ + uint32_t maxsz) \ + { \ + tcg_gen_gvec_3_ool(rd_ofs, rd_ofs, rm_ofs, oprsz, maxsz, \ + DATA, FUNC); \ + } + +#define WRAP_2M_2_OOL_FN(WRAPNAME, FUNC, DATA) \ + static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \ + uint32_t rm_ofs, uint32_t oprsz, \ + uint32_t maxsz) \ + { \ + tcg_gen_gvec_2_ool(rd_ofs, rm_ofs, oprsz, maxsz, DATA, FUNC); \ + } + +WRAP_2M_3_OOL_FN(gen_AESE, gen_helper_crypto_aese, 0) +WRAP_2M_3_OOL_FN(gen_AESD, gen_helper_crypto_aese, 1) +WRAP_2M_2_OOL_FN(gen_AESMC, gen_helper_crypto_aesmc, 0) +WRAP_2M_2_OOL_FN(gen_AESIMC, gen_helper_crypto_aesmc, 1) +WRAP_2M_2_OOL_FN(gen_SHA1H, gen_helper_crypto_sha1h, 0) +WRAP_2M_2_OOL_FN(gen_SHA1SU1, gen_helper_crypto_sha1su1, 0) +WRAP_2M_2_OOL_FN(gen_SHA256SU0, gen_helper_crypto_sha256su0, 0) + +#define DO_2M_CRYPTO(INSN, FEATURE, SIZE) \ + static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ + { \ + if (!dc_isar_feature(FEATURE, s) || a->size != SIZE) { \ + return false; \ + } \ + return do_2misc_vec(s, a, gen_##INSN); \ + } + +DO_2M_CRYPTO(AESE, aa32_aes, 0) +DO_2M_CRYPTO(AESD, aa32_aes, 0) +DO_2M_CRYPTO(AESMC, aa32_aes, 0) +DO_2M_CRYPTO(AESIMC, aa32_aes, 0) +DO_2M_CRYPTO(SHA1H, aa32_sha1, 2) +DO_2M_CRYPTO(SHA1SU1, aa32_sha1, 2) +DO_2M_CRYPTO(SHA256SU0, aa32_sha2, 2) + +static bool do_2misc(DisasContext *s, arg_2misc *a, NeonGenOneOpFn *fn) +{ + int pass; + + /* Handle a 2-reg-misc operation by iterating 32 bits at a time */ + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if (!fn) { + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + for (pass = 0; pass < (a->q ? 4 : 2); pass++) { + TCGv_i32 tmp = neon_load_reg(a->vm, pass); + fn(tmp, tmp); + neon_store_reg(a->vd, pass, tmp); + } + + return true; +} + +static bool trans_VREV32(DisasContext *s, arg_2misc *a) +{ + static NeonGenOneOpFn * const fn[] = { + tcg_gen_bswap32_i32, + gen_swap_half, + NULL, + NULL, + }; + return do_2misc(s, a, fn[a->size]); +} + +static bool trans_VREV16(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0) { + return false; + } + return do_2misc(s, a, gen_rev16); +} + +static bool trans_VCLS(DisasContext *s, arg_2misc *a) +{ + static NeonGenOneOpFn * const fn[] = { + gen_helper_neon_cls_s8, + gen_helper_neon_cls_s16, + gen_helper_neon_cls_s32, + NULL, + }; + return do_2misc(s, a, fn[a->size]); +} + +static void do_VCLZ_32(TCGv_i32 rd, TCGv_i32 rm) +{ + tcg_gen_clzi_i32(rd, rm, 32); +} + +static bool trans_VCLZ(DisasContext *s, arg_2misc *a) +{ + static NeonGenOneOpFn * const fn[] = { + gen_helper_neon_clz_u8, + gen_helper_neon_clz_u16, + do_VCLZ_32, + NULL, + }; + return do_2misc(s, a, fn[a->size]); +} + +static bool trans_VCNT(DisasContext *s, arg_2misc *a) +{ + if (a->size != 0) { + return false; + } + return do_2misc(s, a, gen_helper_neon_cnt_u8); +} + +static bool trans_VABS_F(DisasContext *s, arg_2misc *a) +{ + if (a->size != 2) { + return false; + } + /* TODO: FP16 : size == 1 */ + return do_2misc(s, a, gen_helper_vfp_abss); +} + +static bool trans_VNEG_F(DisasContext *s, arg_2misc *a) +{ + if (a->size != 2) { + return false; + } + /* TODO: FP16 : size == 1 */ + return do_2misc(s, a, gen_helper_vfp_negs); +} + +static bool trans_VRECPE(DisasContext *s, arg_2misc *a) +{ + if (a->size != 2) { + return false; + } + return do_2misc(s, a, gen_helper_recpe_u32); +} + +static bool trans_VRSQRTE(DisasContext *s, arg_2misc *a) +{ + if (a->size != 2) { + return false; + } + return do_2misc(s, a, gen_helper_rsqrte_u32); +} + +#define WRAP_1OP_ENV_FN(WRAPNAME, FUNC) \ + static void WRAPNAME(TCGv_i32 d, TCGv_i32 m) \ + { \ + FUNC(d, cpu_env, m); \ + } + +WRAP_1OP_ENV_FN(gen_VQABS_s8, gen_helper_neon_qabs_s8) +WRAP_1OP_ENV_FN(gen_VQABS_s16, gen_helper_neon_qabs_s16) +WRAP_1OP_ENV_FN(gen_VQABS_s32, gen_helper_neon_qabs_s32) +WRAP_1OP_ENV_FN(gen_VQNEG_s8, gen_helper_neon_qneg_s8) +WRAP_1OP_ENV_FN(gen_VQNEG_s16, gen_helper_neon_qneg_s16) +WRAP_1OP_ENV_FN(gen_VQNEG_s32, gen_helper_neon_qneg_s32) + +static bool trans_VQABS(DisasContext *s, arg_2misc *a) +{ + static NeonGenOneOpFn * const fn[] = { + gen_VQABS_s8, + gen_VQABS_s16, + gen_VQABS_s32, + NULL, + }; + return do_2misc(s, a, fn[a->size]); +} + +static bool trans_VQNEG(DisasContext *s, arg_2misc *a) +{ + static NeonGenOneOpFn * const fn[] = { + gen_VQNEG_s8, + gen_VQNEG_s16, + gen_VQNEG_s32, + NULL, + }; + return do_2misc(s, a, fn[a->size]); +} + +static bool do_2misc_fp(DisasContext *s, arg_2misc *a, + NeonGenOneSingleOpFn *fn) +{ + int pass; + TCGv_ptr fpst; + + /* Handle a 2-reg-misc operation by iterating 32 bits at a time */ + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if (a->size != 2) { + /* TODO: FP16 will be the size == 1 case */ + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + fpst = get_fpstatus_ptr(1); + for (pass = 0; pass < (a->q ? 4 : 2); pass++) { + TCGv_i32 tmp = neon_load_reg(a->vm, pass); + fn(tmp, tmp, fpst); + neon_store_reg(a->vd, pass, tmp); + } + tcg_temp_free_ptr(fpst); + + return true; +} + +#define DO_2MISC_FP(INSN, FUNC) \ + static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ + { \ + return do_2misc_fp(s, a, FUNC); \ + } + +DO_2MISC_FP(VRECPE_F, gen_helper_recpe_f32) +DO_2MISC_FP(VRSQRTE_F, gen_helper_rsqrte_f32) +DO_2MISC_FP(VCVT_FS, gen_helper_vfp_sitos) +DO_2MISC_FP(VCVT_FU, gen_helper_vfp_uitos) +DO_2MISC_FP(VCVT_SF, gen_helper_vfp_tosizs) +DO_2MISC_FP(VCVT_UF, gen_helper_vfp_touizs) + +static bool trans_VRINTX(DisasContext *s, arg_2misc *a) +{ + if (!arm_dc_feature(s, ARM_FEATURE_V8)) { + return false; + } + return do_2misc_fp(s, a, gen_helper_rints_exact); +} + +#define WRAP_FP_CMP0_FWD(WRAPNAME, FUNC) \ + static void WRAPNAME(TCGv_i32 d, TCGv_i32 m, TCGv_ptr fpst) \ + { \ + TCGv_i32 zero = tcg_const_i32(0); \ + FUNC(d, m, zero, fpst); \ + tcg_temp_free_i32(zero); \ + } +#define WRAP_FP_CMP0_REV(WRAPNAME, FUNC) \ + static void WRAPNAME(TCGv_i32 d, TCGv_i32 m, TCGv_ptr fpst) \ + { \ + TCGv_i32 zero = tcg_const_i32(0); \ + FUNC(d, zero, m, fpst); \ + tcg_temp_free_i32(zero); \ + } + +#define DO_FP_CMP0(INSN, FUNC, REV) \ + WRAP_FP_CMP0_##REV(gen_##INSN, FUNC) \ + static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ + { \ + return do_2misc_fp(s, a, gen_##INSN); \ + } + +DO_FP_CMP0(VCGT0_F, gen_helper_neon_cgt_f32, FWD) +DO_FP_CMP0(VCGE0_F, gen_helper_neon_cge_f32, FWD) +DO_FP_CMP0(VCEQ0_F, gen_helper_neon_ceq_f32, FWD) +DO_FP_CMP0(VCLE0_F, gen_helper_neon_cge_f32, REV) +DO_FP_CMP0(VCLT0_F, gen_helper_neon_cgt_f32, REV) + +static bool do_vrint(DisasContext *s, arg_2misc *a, int rmode) +{ + /* + * Handle a VRINT* operation by iterating 32 bits at a time, + * with a specified rounding mode in operation. + */ + int pass; + TCGv_ptr fpst; + TCGv_i32 tcg_rmode; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON) || + !arm_dc_feature(s, ARM_FEATURE_V8)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if (a->size != 2) { + /* TODO: FP16 will be the size == 1 case */ + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + fpst = get_fpstatus_ptr(1); + tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); + gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, cpu_env); + for (pass = 0; pass < (a->q ? 4 : 2); pass++) { + TCGv_i32 tmp = neon_load_reg(a->vm, pass); + gen_helper_rints(tmp, tmp, fpst); + neon_store_reg(a->vd, pass, tmp); + } + gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, cpu_env); + tcg_temp_free_i32(tcg_rmode); + tcg_temp_free_ptr(fpst); + + return true; +} + +#define DO_VRINT(INSN, RMODE) \ + static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ + { \ + return do_vrint(s, a, RMODE); \ + } + +DO_VRINT(VRINTN, FPROUNDING_TIEEVEN) +DO_VRINT(VRINTA, FPROUNDING_TIEAWAY) +DO_VRINT(VRINTZ, FPROUNDING_ZERO) +DO_VRINT(VRINTM, FPROUNDING_NEGINF) +DO_VRINT(VRINTP, FPROUNDING_POSINF) + +static bool do_vcvt(DisasContext *s, arg_2misc *a, int rmode, bool is_signed) +{ + /* + * Handle a VCVT* operation by iterating 32 bits at a time, + * with a specified rounding mode in operation. + */ + int pass; + TCGv_ptr fpst; + TCGv_i32 tcg_rmode, tcg_shift; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON) || + !arm_dc_feature(s, ARM_FEATURE_V8)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if (a->size != 2) { + /* TODO: FP16 will be the size == 1 case */ + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + fpst = get_fpstatus_ptr(1); + tcg_shift = tcg_const_i32(0); + tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); + gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, cpu_env); + for (pass = 0; pass < (a->q ? 4 : 2); pass++) { + TCGv_i32 tmp = neon_load_reg(a->vm, pass); + if (is_signed) { + gen_helper_vfp_tosls(tmp, tmp, tcg_shift, fpst); + } else { + gen_helper_vfp_touls(tmp, tmp, tcg_shift, fpst); + } + neon_store_reg(a->vd, pass, tmp); + } + gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, cpu_env); + tcg_temp_free_i32(tcg_rmode); + tcg_temp_free_i32(tcg_shift); + tcg_temp_free_ptr(fpst); + + return true; +} + +#define DO_VCVT(INSN, RMODE, SIGNED) \ + static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ + { \ + return do_vcvt(s, a, RMODE, SIGNED); \ + } + +DO_VCVT(VCVTAU, FPROUNDING_TIEAWAY, false) +DO_VCVT(VCVTAS, FPROUNDING_TIEAWAY, true) +DO_VCVT(VCVTNU, FPROUNDING_TIEEVEN, false) +DO_VCVT(VCVTNS, FPROUNDING_TIEEVEN, true) +DO_VCVT(VCVTPU, FPROUNDING_POSINF, false) +DO_VCVT(VCVTPS, FPROUNDING_POSINF, true) +DO_VCVT(VCVTMU, FPROUNDING_NEGINF, false) +DO_VCVT(VCVTMS, FPROUNDING_NEGINF, true) + +static bool trans_VSWP(DisasContext *s, arg_2misc *a) +{ + TCGv_i64 rm, rd; + int pass; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if (a->size != 0) { + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + rm = tcg_temp_new_i64(); + rd = tcg_temp_new_i64(); + for (pass = 0; pass < (a->q ? 2 : 1); pass++) { + neon_load_reg64(rm, a->vm + pass); + neon_load_reg64(rd, a->vd + pass); + neon_store_reg64(rm, a->vd + pass); + neon_store_reg64(rd, a->vm + pass); + } + tcg_temp_free_i64(rm); + tcg_temp_free_i64(rd); + + return true; +} +static void gen_neon_trn_u8(TCGv_i32 t0, TCGv_i32 t1) +{ + TCGv_i32 rd, tmp; + + rd = tcg_temp_new_i32(); + tmp = tcg_temp_new_i32(); + + tcg_gen_shli_i32(rd, t0, 8); + tcg_gen_andi_i32(rd, rd, 0xff00ff00); + tcg_gen_andi_i32(tmp, t1, 0x00ff00ff); + tcg_gen_or_i32(rd, rd, tmp); + + tcg_gen_shri_i32(t1, t1, 8); + tcg_gen_andi_i32(t1, t1, 0x00ff00ff); + tcg_gen_andi_i32(tmp, t0, 0xff00ff00); + tcg_gen_or_i32(t1, t1, tmp); + tcg_gen_mov_i32(t0, rd); + + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(rd); +} + +static void gen_neon_trn_u16(TCGv_i32 t0, TCGv_i32 t1) +{ + TCGv_i32 rd, tmp; + + rd = tcg_temp_new_i32(); + tmp = tcg_temp_new_i32(); + + tcg_gen_shli_i32(rd, t0, 16); + tcg_gen_andi_i32(tmp, t1, 0xffff); + tcg_gen_or_i32(rd, rd, tmp); + tcg_gen_shri_i32(t1, t1, 16); + tcg_gen_andi_i32(tmp, t0, 0xffff0000); + tcg_gen_or_i32(t1, t1, tmp); + tcg_gen_mov_i32(t0, rd); + + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(rd); +} + +static bool trans_VTRN(DisasContext *s, arg_2misc *a) +{ + TCGv_i32 tmp, tmp2; + int pass; + + if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { + return false; + } + + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (!dc_isar_feature(aa32_simd_r32, s) && + ((a->vd | a->vm) & 0x10)) { + return false; + } + + if ((a->vd | a->vm) & a->q) { + return false; + } + + if (a->size == 3) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + if (a->size == 2) { + for (pass = 0; pass < (a->q ? 4 : 2); pass += 2) { + tmp = neon_load_reg(a->vm, pass); + tmp2 = neon_load_reg(a->vd, pass + 1); + neon_store_reg(a->vm, pass, tmp2); + neon_store_reg(a->vd, pass + 1, tmp); + } + } else { + for (pass = 0; pass < (a->q ? 4 : 2); pass++) { + tmp = neon_load_reg(a->vm, pass); + tmp2 = neon_load_reg(a->vd, pass); + if (a->size == 0) { + gen_neon_trn_u8(tmp, tmp2); + } else { + gen_neon_trn_u16(tmp, tmp2); + } + neon_store_reg(a->vm, pass, tmp2); + neon_store_reg(a->vd, pass, tmp); + } + } + return true; +} diff --git a/target/arm/translate-vfp.inc.c b/target/arm/translate-vfp.inc.c index e1a9017598..bf31b18657 100644 --- a/target/arm/translate-vfp.inc.c +++ b/target/arm/translate-vfp.inc.c @@ -119,15 +119,14 @@ static bool full_vfp_access_check(DisasContext *s, bool ignore_vfp_enabled) if (s->v7m_lspact) { /* * Lazy state saving affects external memory and also the NVIC, - * so we must mark it as an IO operation for icount. + * so we must mark it as an IO operation for icount (and cause + * this to be the last insn in the TB). */ if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + s->base.is_jmp = DISAS_UPDATE; gen_io_start(); } gen_helper_v7m_preserve_fp_state(cpu_env); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - } /* * If the preserve_fp_state helper doesn't throw an exception * then it will clear LSPACT; we don't need to repeat this for diff --git a/target/arm/translate.c b/target/arm/translate.c index 6d18892ade..795964da1f 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -378,9 +378,9 @@ static void gen_revsh(TCGv_i32 dest, TCGv_i32 var) } /* Swap low and high halfwords. */ -static void gen_swap_half(TCGv_i32 var) +static void gen_swap_half(TCGv_i32 dest, TCGv_i32 var) { - tcg_gen_rotri_i32(var, var, 16); + tcg_gen_rotri_i32(dest, var, 16); } /* Dual 16-bit add. Result placed in t0 and t1 is marked as dead. @@ -1133,25 +1133,6 @@ neon_reg_offset (int reg, int n) return vfp_reg_offset(0, sreg); } -/* Return the offset of a 2**SIZE piece of a NEON register, at index ELE, - * where 0 is the least significant end of the register. - */ -static inline long -neon_element_offset(int reg, int element, MemOp size) -{ - int element_size = 1 << size; - int ofs = element * element_size; -#ifdef HOST_WORDS_BIGENDIAN - /* Calculate the offset assuming fully little-endian, - * then XOR to account for the order of the 8-byte units. - */ - if (element_size < 8) { - ofs ^= 8 - element_size; - } -#endif - return neon_reg_offset(reg, 0) + ofs; -} - static TCGv_i32 neon_load_reg(int reg, int pass) { TCGv_i32 tmp = tcg_temp_new_i32(); @@ -1159,94 +1140,12 @@ static TCGv_i32 neon_load_reg(int reg, int pass) return tmp; } -static void neon_load_element(TCGv_i32 var, int reg, int ele, MemOp mop) -{ - long offset = neon_element_offset(reg, ele, mop & MO_SIZE); - - switch (mop) { - case MO_UB: - tcg_gen_ld8u_i32(var, cpu_env, offset); - break; - case MO_UW: - tcg_gen_ld16u_i32(var, cpu_env, offset); - break; - case MO_UL: - tcg_gen_ld_i32(var, cpu_env, offset); - break; - default: - g_assert_not_reached(); - } -} - -static void neon_load_element64(TCGv_i64 var, int reg, int ele, MemOp mop) -{ - long offset = neon_element_offset(reg, ele, mop & MO_SIZE); - - switch (mop) { - case MO_UB: - tcg_gen_ld8u_i64(var, cpu_env, offset); - break; - case MO_UW: - tcg_gen_ld16u_i64(var, cpu_env, offset); - break; - case MO_UL: - tcg_gen_ld32u_i64(var, cpu_env, offset); - break; - case MO_Q: - tcg_gen_ld_i64(var, cpu_env, offset); - break; - default: - g_assert_not_reached(); - } -} - static void neon_store_reg(int reg, int pass, TCGv_i32 var) { tcg_gen_st_i32(var, cpu_env, neon_reg_offset(reg, pass)); tcg_temp_free_i32(var); } -static void neon_store_element(int reg, int ele, MemOp size, TCGv_i32 var) -{ - long offset = neon_element_offset(reg, ele, size); - - switch (size) { - case MO_8: - tcg_gen_st8_i32(var, cpu_env, offset); - break; - case MO_16: - tcg_gen_st16_i32(var, cpu_env, offset); - break; - case MO_32: - tcg_gen_st_i32(var, cpu_env, offset); - break; - default: - g_assert_not_reached(); - } -} - -static void neon_store_element64(int reg, int ele, MemOp size, TCGv_i64 var) -{ - long offset = neon_element_offset(reg, ele, size); - - switch (size) { - case MO_8: - tcg_gen_st8_i64(var, cpu_env, offset); - break; - case MO_16: - tcg_gen_st16_i64(var, cpu_env, offset); - break; - case MO_32: - tcg_gen_st32_i64(var, cpu_env, offset); - break; - case MO_64: - tcg_gen_st_i64(var, cpu_env, offset); - break; - default: - g_assert_not_reached(); - } -} - static inline void neon_load_reg64(TCGv_i64 var, int reg) { tcg_gen_ld_i64(var, cpu_env, vfp_reg_offset(1, reg)); @@ -2934,377 +2833,6 @@ static void gen_exception_return(DisasContext *s, TCGv_i32 pc) gen_rfe(s, pc, load_cpu_field(spsr)); } -#define CPU_V001 cpu_V0, cpu_V0, cpu_V1 - -static int gen_neon_unzip(int rd, int rm, int size, int q) -{ - TCGv_ptr pd, pm; - - if (!q && size == 2) { - return 1; - } - pd = vfp_reg_ptr(true, rd); - pm = vfp_reg_ptr(true, rm); - if (q) { - switch (size) { - case 0: - gen_helper_neon_qunzip8(pd, pm); - break; - case 1: - gen_helper_neon_qunzip16(pd, pm); - break; - case 2: - gen_helper_neon_qunzip32(pd, pm); - break; - default: - abort(); - } - } else { - switch (size) { - case 0: - gen_helper_neon_unzip8(pd, pm); - break; - case 1: - gen_helper_neon_unzip16(pd, pm); - break; - default: - abort(); - } - } - tcg_temp_free_ptr(pd); - tcg_temp_free_ptr(pm); - return 0; -} - -static int gen_neon_zip(int rd, int rm, int size, int q) -{ - TCGv_ptr pd, pm; - - if (!q && size == 2) { - return 1; - } - pd = vfp_reg_ptr(true, rd); - pm = vfp_reg_ptr(true, rm); - if (q) { - switch (size) { - case 0: - gen_helper_neon_qzip8(pd, pm); - break; - case 1: - gen_helper_neon_qzip16(pd, pm); - break; - case 2: - gen_helper_neon_qzip32(pd, pm); - break; - default: - abort(); - } - } else { - switch (size) { - case 0: - gen_helper_neon_zip8(pd, pm); - break; - case 1: - gen_helper_neon_zip16(pd, pm); - break; - default: - abort(); - } - } - tcg_temp_free_ptr(pd); - tcg_temp_free_ptr(pm); - return 0; -} - -static void gen_neon_trn_u8(TCGv_i32 t0, TCGv_i32 t1) -{ - TCGv_i32 rd, tmp; - - rd = tcg_temp_new_i32(); - tmp = tcg_temp_new_i32(); - - tcg_gen_shli_i32(rd, t0, 8); - tcg_gen_andi_i32(rd, rd, 0xff00ff00); - tcg_gen_andi_i32(tmp, t1, 0x00ff00ff); - tcg_gen_or_i32(rd, rd, tmp); - - tcg_gen_shri_i32(t1, t1, 8); - tcg_gen_andi_i32(t1, t1, 0x00ff00ff); - tcg_gen_andi_i32(tmp, t0, 0xff00ff00); - tcg_gen_or_i32(t1, t1, tmp); - tcg_gen_mov_i32(t0, rd); - - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(rd); -} - -static void gen_neon_trn_u16(TCGv_i32 t0, TCGv_i32 t1) -{ - TCGv_i32 rd, tmp; - - rd = tcg_temp_new_i32(); - tmp = tcg_temp_new_i32(); - - tcg_gen_shli_i32(rd, t0, 16); - tcg_gen_andi_i32(tmp, t1, 0xffff); - tcg_gen_or_i32(rd, rd, tmp); - tcg_gen_shri_i32(t1, t1, 16); - tcg_gen_andi_i32(tmp, t0, 0xffff0000); - tcg_gen_or_i32(t1, t1, tmp); - tcg_gen_mov_i32(t0, rd); - - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(rd); -} - -static inline void gen_neon_narrow(int size, TCGv_i32 dest, TCGv_i64 src) -{ - switch (size) { - case 0: gen_helper_neon_narrow_u8(dest, src); break; - case 1: gen_helper_neon_narrow_u16(dest, src); break; - case 2: tcg_gen_extrl_i64_i32(dest, src); break; - default: abort(); - } -} - -static inline void gen_neon_narrow_sats(int size, TCGv_i32 dest, TCGv_i64 src) -{ - switch (size) { - case 0: gen_helper_neon_narrow_sat_s8(dest, cpu_env, src); break; - case 1: gen_helper_neon_narrow_sat_s16(dest, cpu_env, src); break; - case 2: gen_helper_neon_narrow_sat_s32(dest, cpu_env, src); break; - default: abort(); - } -} - -static inline void gen_neon_narrow_satu(int size, TCGv_i32 dest, TCGv_i64 src) -{ - switch (size) { - case 0: gen_helper_neon_narrow_sat_u8(dest, cpu_env, src); break; - case 1: gen_helper_neon_narrow_sat_u16(dest, cpu_env, src); break; - case 2: gen_helper_neon_narrow_sat_u32(dest, cpu_env, src); break; - default: abort(); - } -} - -static inline void gen_neon_unarrow_sats(int size, TCGv_i32 dest, TCGv_i64 src) -{ - switch (size) { - case 0: gen_helper_neon_unarrow_sat8(dest, cpu_env, src); break; - case 1: gen_helper_neon_unarrow_sat16(dest, cpu_env, src); break; - case 2: gen_helper_neon_unarrow_sat32(dest, cpu_env, src); break; - default: abort(); - } -} - -static inline void gen_neon_widen(TCGv_i64 dest, TCGv_i32 src, int size, int u) -{ - if (u) { - switch (size) { - case 0: gen_helper_neon_widen_u8(dest, src); break; - case 1: gen_helper_neon_widen_u16(dest, src); break; - case 2: tcg_gen_extu_i32_i64(dest, src); break; - default: abort(); - } - } else { - switch (size) { - case 0: gen_helper_neon_widen_s8(dest, src); break; - case 1: gen_helper_neon_widen_s16(dest, src); break; - case 2: tcg_gen_ext_i32_i64(dest, src); break; - default: abort(); - } - } - tcg_temp_free_i32(src); -} - -static inline void gen_neon_addl(int size) -{ - switch (size) { - case 0: gen_helper_neon_addl_u16(CPU_V001); break; - case 1: gen_helper_neon_addl_u32(CPU_V001); break; - case 2: tcg_gen_add_i64(CPU_V001); break; - default: abort(); - } -} - -static void gen_neon_narrow_op(int op, int u, int size, - TCGv_i32 dest, TCGv_i64 src) -{ - if (op) { - if (u) { - gen_neon_unarrow_sats(size, dest, src); - } else { - gen_neon_narrow(size, dest, src); - } - } else { - if (u) { - gen_neon_narrow_satu(size, dest, src); - } else { - gen_neon_narrow_sats(size, dest, src); - } - } -} - -/* Symbolic constants for op fields for Neon 2-register miscellaneous. - * The values correspond to bits [17:16,10:7]; see the ARM ARM DDI0406B - * table A7-13. - */ -#define NEON_2RM_VREV64 0 -#define NEON_2RM_VREV32 1 -#define NEON_2RM_VREV16 2 -#define NEON_2RM_VPADDL 4 -#define NEON_2RM_VPADDL_U 5 -#define NEON_2RM_AESE 6 /* Includes AESD */ -#define NEON_2RM_AESMC 7 /* Includes AESIMC */ -#define NEON_2RM_VCLS 8 -#define NEON_2RM_VCLZ 9 -#define NEON_2RM_VCNT 10 -#define NEON_2RM_VMVN 11 -#define NEON_2RM_VPADAL 12 -#define NEON_2RM_VPADAL_U 13 -#define NEON_2RM_VQABS 14 -#define NEON_2RM_VQNEG 15 -#define NEON_2RM_VCGT0 16 -#define NEON_2RM_VCGE0 17 -#define NEON_2RM_VCEQ0 18 -#define NEON_2RM_VCLE0 19 -#define NEON_2RM_VCLT0 20 -#define NEON_2RM_SHA1H 21 -#define NEON_2RM_VABS 22 -#define NEON_2RM_VNEG 23 -#define NEON_2RM_VCGT0_F 24 -#define NEON_2RM_VCGE0_F 25 -#define NEON_2RM_VCEQ0_F 26 -#define NEON_2RM_VCLE0_F 27 -#define NEON_2RM_VCLT0_F 28 -#define NEON_2RM_VABS_F 30 -#define NEON_2RM_VNEG_F 31 -#define NEON_2RM_VSWP 32 -#define NEON_2RM_VTRN 33 -#define NEON_2RM_VUZP 34 -#define NEON_2RM_VZIP 35 -#define NEON_2RM_VMOVN 36 /* Includes VQMOVN, VQMOVUN */ -#define NEON_2RM_VQMOVN 37 /* Includes VQMOVUN */ -#define NEON_2RM_VSHLL 38 -#define NEON_2RM_SHA1SU1 39 /* Includes SHA256SU0 */ -#define NEON_2RM_VRINTN 40 -#define NEON_2RM_VRINTX 41 -#define NEON_2RM_VRINTA 42 -#define NEON_2RM_VRINTZ 43 -#define NEON_2RM_VCVT_F16_F32 44 -#define NEON_2RM_VRINTM 45 -#define NEON_2RM_VCVT_F32_F16 46 -#define NEON_2RM_VRINTP 47 -#define NEON_2RM_VCVTAU 48 -#define NEON_2RM_VCVTAS 49 -#define NEON_2RM_VCVTNU 50 -#define NEON_2RM_VCVTNS 51 -#define NEON_2RM_VCVTPU 52 -#define NEON_2RM_VCVTPS 53 -#define NEON_2RM_VCVTMU 54 -#define NEON_2RM_VCVTMS 55 -#define NEON_2RM_VRECPE 56 -#define NEON_2RM_VRSQRTE 57 -#define NEON_2RM_VRECPE_F 58 -#define NEON_2RM_VRSQRTE_F 59 -#define NEON_2RM_VCVT_FS 60 -#define NEON_2RM_VCVT_FU 61 -#define NEON_2RM_VCVT_SF 62 -#define NEON_2RM_VCVT_UF 63 - -static bool neon_2rm_is_v8_op(int op) -{ - /* Return true if this neon 2reg-misc op is ARMv8 and up */ - switch (op) { - case NEON_2RM_VRINTN: - case NEON_2RM_VRINTA: - case NEON_2RM_VRINTM: - case NEON_2RM_VRINTP: - case NEON_2RM_VRINTZ: - case NEON_2RM_VRINTX: - case NEON_2RM_VCVTAU: - case NEON_2RM_VCVTAS: - case NEON_2RM_VCVTNU: - case NEON_2RM_VCVTNS: - case NEON_2RM_VCVTPU: - case NEON_2RM_VCVTPS: - case NEON_2RM_VCVTMU: - case NEON_2RM_VCVTMS: - return true; - default: - return false; - } -} - -/* Each entry in this array has bit n set if the insn allows - * size value n (otherwise it will UNDEF). Since unallocated - * op values will have no bits set they always UNDEF. - */ -static const uint8_t neon_2rm_sizes[] = { - [NEON_2RM_VREV64] = 0x7, - [NEON_2RM_VREV32] = 0x3, - [NEON_2RM_VREV16] = 0x1, - [NEON_2RM_VPADDL] = 0x7, - [NEON_2RM_VPADDL_U] = 0x7, - [NEON_2RM_AESE] = 0x1, - [NEON_2RM_AESMC] = 0x1, - [NEON_2RM_VCLS] = 0x7, - [NEON_2RM_VCLZ] = 0x7, - [NEON_2RM_VCNT] = 0x1, - [NEON_2RM_VMVN] = 0x1, - [NEON_2RM_VPADAL] = 0x7, - [NEON_2RM_VPADAL_U] = 0x7, - [NEON_2RM_VQABS] = 0x7, - [NEON_2RM_VQNEG] = 0x7, - [NEON_2RM_VCGT0] = 0x7, - [NEON_2RM_VCGE0] = 0x7, - [NEON_2RM_VCEQ0] = 0x7, - [NEON_2RM_VCLE0] = 0x7, - [NEON_2RM_VCLT0] = 0x7, - [NEON_2RM_SHA1H] = 0x4, - [NEON_2RM_VABS] = 0x7, - [NEON_2RM_VNEG] = 0x7, - [NEON_2RM_VCGT0_F] = 0x4, - [NEON_2RM_VCGE0_F] = 0x4, - [NEON_2RM_VCEQ0_F] = 0x4, - [NEON_2RM_VCLE0_F] = 0x4, - [NEON_2RM_VCLT0_F] = 0x4, - [NEON_2RM_VABS_F] = 0x4, - [NEON_2RM_VNEG_F] = 0x4, - [NEON_2RM_VSWP] = 0x1, - [NEON_2RM_VTRN] = 0x7, - [NEON_2RM_VUZP] = 0x7, - [NEON_2RM_VZIP] = 0x7, - [NEON_2RM_VMOVN] = 0x7, - [NEON_2RM_VQMOVN] = 0x7, - [NEON_2RM_VSHLL] = 0x7, - [NEON_2RM_SHA1SU1] = 0x4, - [NEON_2RM_VRINTN] = 0x4, - [NEON_2RM_VRINTX] = 0x4, - [NEON_2RM_VRINTA] = 0x4, - [NEON_2RM_VRINTZ] = 0x4, - [NEON_2RM_VCVT_F16_F32] = 0x2, - [NEON_2RM_VRINTM] = 0x4, - [NEON_2RM_VCVT_F32_F16] = 0x2, - [NEON_2RM_VRINTP] = 0x4, - [NEON_2RM_VCVTAU] = 0x4, - [NEON_2RM_VCVTAS] = 0x4, - [NEON_2RM_VCVTNU] = 0x4, - [NEON_2RM_VCVTNS] = 0x4, - [NEON_2RM_VCVTPU] = 0x4, - [NEON_2RM_VCVTPS] = 0x4, - [NEON_2RM_VCVTMU] = 0x4, - [NEON_2RM_VCVTMS] = 0x4, - [NEON_2RM_VRECPE] = 0x4, - [NEON_2RM_VRSQRTE] = 0x4, - [NEON_2RM_VRECPE_F] = 0x4, - [NEON_2RM_VRSQRTE_F] = 0x4, - [NEON_2RM_VCVT_FS] = 0x4, - [NEON_2RM_VCVT_FU] = 0x4, - [NEON_2RM_VCVT_SF] = 0x4, - [NEON_2RM_VCVT_UF] = 0x4, -}; - static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz, gen_helper_gvec_3_ptr *fn) @@ -5016,573 +4544,6 @@ void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } -/* Translate a NEON data processing instruction. Return nonzero if the - instruction is invalid. - We process data in a mixture of 32-bit and 64-bit chunks. - Mostly we use 32-bit chunks so we can use normal scalar instructions. */ - -static int disas_neon_data_insn(DisasContext *s, uint32_t insn) -{ - int op; - int q; - int rd, rm, rd_ofs, rm_ofs; - int size; - int pass; - int u; - int vec_size; - TCGv_i32 tmp, tmp2, tmp3; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return 1; - } - - /* FIXME: this access check should not take precedence over UNDEF - * for invalid encodings; we will generate incorrect syndrome information - * for attempts to execute invalid vfp/neon encodings with FP disabled. - */ - if (s->fp_excp_el) { - gen_exception_insn(s, s->pc_curr, EXCP_UDEF, - syn_simd_access_trap(1, 0xe, false), s->fp_excp_el); - return 0; - } - - if (!s->vfp_enabled) - return 1; - q = (insn & (1 << 6)) != 0; - u = (insn >> 24) & 1; - VFP_DREG_D(rd, insn); - VFP_DREG_M(rm, insn); - size = (insn >> 20) & 3; - vec_size = q ? 16 : 8; - rd_ofs = neon_reg_offset(rd, 0); - rm_ofs = neon_reg_offset(rm, 0); - - if ((insn & (1 << 23)) == 0) { - /* Three register same length: handled by decodetree */ - return 1; - } else if (insn & (1 << 4)) { - /* Two registers and shift or reg and imm: handled by decodetree */ - return 1; - } else { /* (insn & 0x00800010 == 0x00800000) */ - if (size != 3) { - /* - * Three registers of different lengths, or two registers and - * a scalar: handled by decodetree - */ - return 1; - } else { /* size == 3 */ - if (!u) { - /* Extract: handled by decodetree */ - return 1; - } else if ((insn & (1 << 11)) == 0) { - /* Two register misc. */ - op = ((insn >> 12) & 0x30) | ((insn >> 7) & 0xf); - size = (insn >> 18) & 3; - /* UNDEF for unknown op values and bad op-size combinations */ - if ((neon_2rm_sizes[op] & (1 << size)) == 0) { - return 1; - } - if (neon_2rm_is_v8_op(op) && - !arm_dc_feature(s, ARM_FEATURE_V8)) { - return 1; - } - if ((op != NEON_2RM_VMOVN && op != NEON_2RM_VQMOVN) && - q && ((rm | rd) & 1)) { - return 1; - } - switch (op) { - case NEON_2RM_VREV64: - for (pass = 0; pass < (q ? 2 : 1); pass++) { - tmp = neon_load_reg(rm, pass * 2); - tmp2 = neon_load_reg(rm, pass * 2 + 1); - switch (size) { - case 0: tcg_gen_bswap32_i32(tmp, tmp); break; - case 1: gen_swap_half(tmp); break; - case 2: /* no-op */ break; - default: abort(); - } - neon_store_reg(rd, pass * 2 + 1, tmp); - if (size == 2) { - neon_store_reg(rd, pass * 2, tmp2); - } else { - switch (size) { - case 0: tcg_gen_bswap32_i32(tmp2, tmp2); break; - case 1: gen_swap_half(tmp2); break; - default: abort(); - } - neon_store_reg(rd, pass * 2, tmp2); - } - } - break; - case NEON_2RM_VPADDL: case NEON_2RM_VPADDL_U: - case NEON_2RM_VPADAL: case NEON_2RM_VPADAL_U: - for (pass = 0; pass < q + 1; pass++) { - tmp = neon_load_reg(rm, pass * 2); - gen_neon_widen(cpu_V0, tmp, size, op & 1); - tmp = neon_load_reg(rm, pass * 2 + 1); - gen_neon_widen(cpu_V1, tmp, size, op & 1); - switch (size) { - case 0: gen_helper_neon_paddl_u16(CPU_V001); break; - case 1: gen_helper_neon_paddl_u32(CPU_V001); break; - case 2: tcg_gen_add_i64(CPU_V001); break; - default: abort(); - } - if (op >= NEON_2RM_VPADAL) { - /* Accumulate. */ - neon_load_reg64(cpu_V1, rd + pass); - gen_neon_addl(size); - } - neon_store_reg64(cpu_V0, rd + pass); - } - break; - case NEON_2RM_VTRN: - if (size == 2) { - int n; - for (n = 0; n < (q ? 4 : 2); n += 2) { - tmp = neon_load_reg(rm, n); - tmp2 = neon_load_reg(rd, n + 1); - neon_store_reg(rm, n, tmp2); - neon_store_reg(rd, n + 1, tmp); - } - } else { - goto elementwise; - } - break; - case NEON_2RM_VUZP: - if (gen_neon_unzip(rd, rm, size, q)) { - return 1; - } - break; - case NEON_2RM_VZIP: - if (gen_neon_zip(rd, rm, size, q)) { - return 1; - } - break; - case NEON_2RM_VMOVN: case NEON_2RM_VQMOVN: - /* also VQMOVUN; op field and mnemonics don't line up */ - if (rm & 1) { - return 1; - } - tmp2 = NULL; - for (pass = 0; pass < 2; pass++) { - neon_load_reg64(cpu_V0, rm + pass); - tmp = tcg_temp_new_i32(); - gen_neon_narrow_op(op == NEON_2RM_VMOVN, q, size, - tmp, cpu_V0); - if (pass == 0) { - tmp2 = tmp; - } else { - neon_store_reg(rd, 0, tmp2); - neon_store_reg(rd, 1, tmp); - } - } - break; - case NEON_2RM_VSHLL: - if (q || (rd & 1)) { - return 1; - } - tmp = neon_load_reg(rm, 0); - tmp2 = neon_load_reg(rm, 1); - for (pass = 0; pass < 2; pass++) { - if (pass == 1) - tmp = tmp2; - gen_neon_widen(cpu_V0, tmp, size, 1); - tcg_gen_shli_i64(cpu_V0, cpu_V0, 8 << size); - neon_store_reg64(cpu_V0, rd + pass); - } - break; - case NEON_2RM_VCVT_F16_F32: - { - TCGv_ptr fpst; - TCGv_i32 ahp; - - if (!dc_isar_feature(aa32_fp16_spconv, s) || - q || (rm & 1)) { - return 1; - } - fpst = get_fpstatus_ptr(true); - ahp = get_ahp_flag(); - tmp = neon_load_reg(rm, 0); - gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp); - tmp2 = neon_load_reg(rm, 1); - gen_helper_vfp_fcvt_f32_to_f16(tmp2, tmp2, fpst, ahp); - tcg_gen_shli_i32(tmp2, tmp2, 16); - tcg_gen_or_i32(tmp2, tmp2, tmp); - tcg_temp_free_i32(tmp); - tmp = neon_load_reg(rm, 2); - gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp); - tmp3 = neon_load_reg(rm, 3); - neon_store_reg(rd, 0, tmp2); - gen_helper_vfp_fcvt_f32_to_f16(tmp3, tmp3, fpst, ahp); - tcg_gen_shli_i32(tmp3, tmp3, 16); - tcg_gen_or_i32(tmp3, tmp3, tmp); - neon_store_reg(rd, 1, tmp3); - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(ahp); - tcg_temp_free_ptr(fpst); - break; - } - case NEON_2RM_VCVT_F32_F16: - { - TCGv_ptr fpst; - TCGv_i32 ahp; - if (!dc_isar_feature(aa32_fp16_spconv, s) || - q || (rd & 1)) { - return 1; - } - fpst = get_fpstatus_ptr(true); - ahp = get_ahp_flag(); - tmp3 = tcg_temp_new_i32(); - tmp = neon_load_reg(rm, 0); - tmp2 = neon_load_reg(rm, 1); - tcg_gen_ext16u_i32(tmp3, tmp); - gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp); - neon_store_reg(rd, 0, tmp3); - tcg_gen_shri_i32(tmp, tmp, 16); - gen_helper_vfp_fcvt_f16_to_f32(tmp, tmp, fpst, ahp); - neon_store_reg(rd, 1, tmp); - tmp3 = tcg_temp_new_i32(); - tcg_gen_ext16u_i32(tmp3, tmp2); - gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp); - neon_store_reg(rd, 2, tmp3); - tcg_gen_shri_i32(tmp2, tmp2, 16); - gen_helper_vfp_fcvt_f16_to_f32(tmp2, tmp2, fpst, ahp); - neon_store_reg(rd, 3, tmp2); - tcg_temp_free_i32(ahp); - tcg_temp_free_ptr(fpst); - break; - } - case NEON_2RM_AESE: case NEON_2RM_AESMC: - if (!dc_isar_feature(aa32_aes, s) || ((rm | rd) & 1)) { - return 1; - } - /* - * Bit 6 is the lowest opcode bit; it distinguishes - * between encryption (AESE/AESMC) and decryption - * (AESD/AESIMC). - */ - if (op == NEON_2RM_AESE) { - tcg_gen_gvec_3_ool(vfp_reg_offset(true, rd), - vfp_reg_offset(true, rd), - vfp_reg_offset(true, rm), - 16, 16, extract32(insn, 6, 1), - gen_helper_crypto_aese); - } else { - tcg_gen_gvec_2_ool(vfp_reg_offset(true, rd), - vfp_reg_offset(true, rm), - 16, 16, extract32(insn, 6, 1), - gen_helper_crypto_aesmc); - } - break; - case NEON_2RM_SHA1H: - if (!dc_isar_feature(aa32_sha1, s) || ((rm | rd) & 1)) { - return 1; - } - tcg_gen_gvec_2_ool(rd_ofs, rm_ofs, 16, 16, 0, - gen_helper_crypto_sha1h); - break; - case NEON_2RM_SHA1SU1: - if ((rm | rd) & 1) { - return 1; - } - /* bit 6 (q): set -> SHA256SU0, cleared -> SHA1SU1 */ - if (q) { - if (!dc_isar_feature(aa32_sha2, s)) { - return 1; - } - } else if (!dc_isar_feature(aa32_sha1, s)) { - return 1; - } - tcg_gen_gvec_2_ool(rd_ofs, rm_ofs, 16, 16, 0, - q ? gen_helper_crypto_sha256su0 - : gen_helper_crypto_sha1su1); - break; - case NEON_2RM_VMVN: - tcg_gen_gvec_not(0, rd_ofs, rm_ofs, vec_size, vec_size); - break; - case NEON_2RM_VNEG: - tcg_gen_gvec_neg(size, rd_ofs, rm_ofs, vec_size, vec_size); - break; - case NEON_2RM_VABS: - tcg_gen_gvec_abs(size, rd_ofs, rm_ofs, vec_size, vec_size); - break; - - case NEON_2RM_VCEQ0: - gen_gvec_ceq0(size, rd_ofs, rm_ofs, vec_size, vec_size); - break; - case NEON_2RM_VCGT0: - gen_gvec_cgt0(size, rd_ofs, rm_ofs, vec_size, vec_size); - break; - case NEON_2RM_VCLE0: - gen_gvec_cle0(size, rd_ofs, rm_ofs, vec_size, vec_size); - break; - case NEON_2RM_VCGE0: - gen_gvec_cge0(size, rd_ofs, rm_ofs, vec_size, vec_size); - break; - case NEON_2RM_VCLT0: - gen_gvec_clt0(size, rd_ofs, rm_ofs, vec_size, vec_size); - break; - - default: - elementwise: - for (pass = 0; pass < (q ? 4 : 2); pass++) { - tmp = neon_load_reg(rm, pass); - switch (op) { - case NEON_2RM_VREV32: - switch (size) { - case 0: tcg_gen_bswap32_i32(tmp, tmp); break; - case 1: gen_swap_half(tmp); break; - default: abort(); - } - break; - case NEON_2RM_VREV16: - gen_rev16(tmp, tmp); - break; - case NEON_2RM_VCLS: - switch (size) { - case 0: gen_helper_neon_cls_s8(tmp, tmp); break; - case 1: gen_helper_neon_cls_s16(tmp, tmp); break; - case 2: gen_helper_neon_cls_s32(tmp, tmp); break; - default: abort(); - } - break; - case NEON_2RM_VCLZ: - switch (size) { - case 0: gen_helper_neon_clz_u8(tmp, tmp); break; - case 1: gen_helper_neon_clz_u16(tmp, tmp); break; - case 2: tcg_gen_clzi_i32(tmp, tmp, 32); break; - default: abort(); - } - break; - case NEON_2RM_VCNT: - gen_helper_neon_cnt_u8(tmp, tmp); - break; - case NEON_2RM_VQABS: - switch (size) { - case 0: - gen_helper_neon_qabs_s8(tmp, cpu_env, tmp); - break; - case 1: - gen_helper_neon_qabs_s16(tmp, cpu_env, tmp); - break; - case 2: - gen_helper_neon_qabs_s32(tmp, cpu_env, tmp); - break; - default: abort(); - } - break; - case NEON_2RM_VQNEG: - switch (size) { - case 0: - gen_helper_neon_qneg_s8(tmp, cpu_env, tmp); - break; - case 1: - gen_helper_neon_qneg_s16(tmp, cpu_env, tmp); - break; - case 2: - gen_helper_neon_qneg_s32(tmp, cpu_env, tmp); - break; - default: abort(); - } - break; - case NEON_2RM_VCGT0_F: - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - tmp2 = tcg_const_i32(0); - gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus); - tcg_temp_free_i32(tmp2); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCGE0_F: - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - tmp2 = tcg_const_i32(0); - gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus); - tcg_temp_free_i32(tmp2); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCEQ0_F: - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - tmp2 = tcg_const_i32(0); - gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus); - tcg_temp_free_i32(tmp2); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCLE0_F: - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - tmp2 = tcg_const_i32(0); - gen_helper_neon_cge_f32(tmp, tmp2, tmp, fpstatus); - tcg_temp_free_i32(tmp2); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCLT0_F: - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - tmp2 = tcg_const_i32(0); - gen_helper_neon_cgt_f32(tmp, tmp2, tmp, fpstatus); - tcg_temp_free_i32(tmp2); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VABS_F: - gen_helper_vfp_abss(tmp, tmp); - break; - case NEON_2RM_VNEG_F: - gen_helper_vfp_negs(tmp, tmp); - break; - case NEON_2RM_VSWP: - tmp2 = neon_load_reg(rd, pass); - neon_store_reg(rm, pass, tmp2); - break; - case NEON_2RM_VTRN: - tmp2 = neon_load_reg(rd, pass); - switch (size) { - case 0: gen_neon_trn_u8(tmp, tmp2); break; - case 1: gen_neon_trn_u16(tmp, tmp2); break; - default: abort(); - } - neon_store_reg(rm, pass, tmp2); - break; - case NEON_2RM_VRINTN: - case NEON_2RM_VRINTA: - case NEON_2RM_VRINTM: - case NEON_2RM_VRINTP: - case NEON_2RM_VRINTZ: - { - TCGv_i32 tcg_rmode; - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - int rmode; - - if (op == NEON_2RM_VRINTZ) { - rmode = FPROUNDING_ZERO; - } else { - rmode = fp_decode_rm[((op & 0x6) >> 1) ^ 1]; - } - - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, - cpu_env); - gen_helper_rints(tmp, tmp, fpstatus); - gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, - cpu_env); - tcg_temp_free_ptr(fpstatus); - tcg_temp_free_i32(tcg_rmode); - break; - } - case NEON_2RM_VRINTX: - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - gen_helper_rints_exact(tmp, tmp, fpstatus); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCVTAU: - case NEON_2RM_VCVTAS: - case NEON_2RM_VCVTNU: - case NEON_2RM_VCVTNS: - case NEON_2RM_VCVTPU: - case NEON_2RM_VCVTPS: - case NEON_2RM_VCVTMU: - case NEON_2RM_VCVTMS: - { - bool is_signed = !extract32(insn, 7, 1); - TCGv_ptr fpst = get_fpstatus_ptr(1); - TCGv_i32 tcg_rmode, tcg_shift; - int rmode = fp_decode_rm[extract32(insn, 8, 2)]; - - tcg_shift = tcg_const_i32(0); - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, - cpu_env); - - if (is_signed) { - gen_helper_vfp_tosls(tmp, tmp, - tcg_shift, fpst); - } else { - gen_helper_vfp_touls(tmp, tmp, - tcg_shift, fpst); - } - - gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, - cpu_env); - tcg_temp_free_i32(tcg_rmode); - tcg_temp_free_i32(tcg_shift); - tcg_temp_free_ptr(fpst); - break; - } - case NEON_2RM_VRECPE: - gen_helper_recpe_u32(tmp, tmp); - break; - case NEON_2RM_VRSQRTE: - gen_helper_rsqrte_u32(tmp, tmp); - break; - case NEON_2RM_VRECPE_F: - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - gen_helper_recpe_f32(tmp, tmp, fpstatus); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VRSQRTE_F: - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - gen_helper_rsqrte_f32(tmp, tmp, fpstatus); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCVT_FS: /* VCVT.F32.S32 */ - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - gen_helper_vfp_sitos(tmp, tmp, fpstatus); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCVT_FU: /* VCVT.F32.U32 */ - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - gen_helper_vfp_uitos(tmp, tmp, fpstatus); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCVT_SF: /* VCVT.S32.F32 */ - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - gen_helper_vfp_tosizs(tmp, tmp, fpstatus); - tcg_temp_free_ptr(fpstatus); - break; - } - case NEON_2RM_VCVT_UF: /* VCVT.U32.F32 */ - { - TCGv_ptr fpstatus = get_fpstatus_ptr(1); - gen_helper_vfp_touizs(tmp, tmp, fpstatus); - tcg_temp_free_ptr(fpstatus); - break; - } - default: - /* Reserved op values were caught by the - * neon_2rm_sizes[] check earlier. - */ - abort(); - } - neon_store_reg(rd, pass, tmp); - } - break; - } - } else { - /* VTBL, VTBX, VDUP: handled by decodetree */ - return 1; - } - } - } - return 0; -} - static int disas_coproc_insn(DisasContext *s, uint32_t insn) { int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2; @@ -8417,7 +7378,7 @@ static bool op_smlad(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); if (m_swap) { - gen_swap_half(t2); + gen_swap_half(t2, t2); } gen_smul_dual(t1, t2); @@ -8475,7 +7436,7 @@ static bool op_smlald(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) t1 = load_reg(s, a->rn); t2 = load_reg(s, a->rm); if (m_swap) { - gen_swap_half(t2); + gen_swap_half(t2, t2); } gen_smul_dual(t1, t2); @@ -8824,9 +7785,6 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) gen_io_start(); } gen_helper_cpsr_write_eret(cpu_env, tmp); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - } tcg_temp_free_i32(tmp); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; @@ -9283,13 +8241,6 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } /* fall back to legacy decoder */ - if (((insn >> 25) & 7) == 1) { - /* NEON Data processing. */ - if (disas_neon_data_insn(s, insn)) { - goto illegal_op; - } - return; - } if ((insn & 0x0e000f00) == 0x0c000100) { if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { /* iWMMXt register transfer. */ @@ -9477,11 +8428,8 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) break; } if (((insn >> 24) & 3) == 3) { - /* Translate into the equivalent ARM encoding. */ - insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28); - if (disas_neon_data_insn(s, insn)) { - goto illegal_op; - } + /* Neon DP, but failed disas_neon_dp() */ + goto illegal_op; } else if (((insn >> 8) & 0xe) == 10) { /* VFP, but failed disas_vfp. */ goto illegal_op; diff --git a/target/arm/translate.h b/target/arm/translate.h index 62ed5c4780..19650a9e2d 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -363,6 +363,7 @@ typedef void GVecGen4Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); /* Function prototype for gen_ functions for calling Neon helpers */ +typedef void NeonGenOneOpFn(TCGv_i32, TCGv_i32); typedef void NeonGenOneOpEnvFn(TCGv_i32, TCGv_ptr, TCGv_i32); typedef void NeonGenTwoOpFn(TCGv_i32, TCGv_i32, TCGv_i32); typedef void NeonGenTwoOpEnvFn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32); @@ -372,9 +373,10 @@ typedef void NeonGenNarrowFn(TCGv_i32, TCGv_i64); typedef void NeonGenNarrowEnvFn(TCGv_i32, TCGv_ptr, TCGv_i64); typedef void NeonGenWidenFn(TCGv_i64, TCGv_i32); typedef void NeonGenTwoOpWidenFn(TCGv_i64, TCGv_i32, TCGv_i32); -typedef void NeonGenTwoSingleOPFn(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); -typedef void NeonGenTwoDoubleOPFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr); -typedef void NeonGenOneOpFn(TCGv_i64, TCGv_i64); +typedef void NeonGenOneSingleOpFn(TCGv_i32, TCGv_i32, TCGv_ptr); +typedef void NeonGenTwoSingleOpFn(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); +typedef void NeonGenTwoDoubleOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr); +typedef void NeonGenOne64OpFn(TCGv_i64, TCGv_i64); typedef void CryptoTwoOpFn(TCGv_ptr, TCGv_ptr); typedef void CryptoThreeOpIntFn(TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void CryptoThreeOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 3a6d202d03..391a0b9eec 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -126,9 +126,7 @@ static void riscv_any_cpu_init(Object *obj) set_resetvec(env, DEFAULT_RSTVEC); } -#if defined(TARGET_RISCV32) - -static void riscv_base32_cpu_init(Object *obj) +static void riscv_base_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; /* We set this in the realise function */ @@ -136,63 +134,38 @@ static void riscv_base32_cpu_init(Object *obj) set_resetvec(env, DEFAULT_RSTVEC); } -static void rv32gcsu_priv1_10_0_cpu_init(Object *obj) +static void rvxx_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, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); - set_resetvec(env, DEFAULT_RSTVEC); + set_resetvec(env, 0x1004); } -static void rv32imcu_nommu_cpu_init(Object *obj) +static void rvxx_sifive_e_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV32 | RVI | RVM | RVC | RVU); + set_misa(env, RVXLEN | RVI | RVM | RVA | RVC | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); - set_resetvec(env, 0x8090); + set_resetvec(env, 0x1004); qdev_prop_set_bit(DEVICE(obj), "mmu", false); } -static void rv32imacu_nommu_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - set_resetvec(env, DEFAULT_RSTVEC); - qdev_prop_set_bit(DEVICE(obj), "mmu", false); -} +#if defined(TARGET_RISCV32) -static void rv32imafcu_nommu_cpu_init(Object *obj) +static void rv32_ibex_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVC | RVU); + set_misa(env, RV32 | RVI | RVM | RVC | RVU); set_priv_version(env, PRIV_VERSION_1_10_0); - set_resetvec(env, DEFAULT_RSTVEC); + set_resetvec(env, 0x8090); qdev_prop_set_bit(DEVICE(obj), "mmu", false); } -#elif defined(TARGET_RISCV64) - -static void riscv_base64_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - /* We set this in the realise function */ - set_misa(env, 0); - set_resetvec(env, DEFAULT_RSTVEC); -} - -static void rv64gcsu_priv1_10_0_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - set_resetvec(env, DEFAULT_RSTVEC); -} - -static void rv64imacu_nommu_cpu_init(Object *obj) +static void rv32_imafcu_nommu_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU); + set_misa(env, 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); @@ -603,15 +576,15 @@ static const TypeInfo riscv_cpu_type_infos[] = { }, DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init), #if defined(TARGET_RISCV32) - DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base32_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_IBEX, rv32imcu_nommu_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34, rv32imafcu_nommu_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_IBEX, rv32_ibex_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rvxx_sifive_e_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34, rv32_imafcu_nommu_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rvxx_sifive_u_cpu_init), #elif defined(TARGET_RISCV64) - DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base64_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rvxx_sifive_e_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rvxx_sifive_u_cpu_init), #endif }; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 62fe1ecc8f..75d2ae3434 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -435,8 +435,13 @@ restart: hwaddr vbase; /* Do the second stage translation on the base PTE address. */ - get_physical_address(env, &vbase, &vbase_prot, base, access_type, - mmu_idx, false, true); + int vbase_ret = get_physical_address(env, &vbase, &vbase_prot, + base, MMU_DATA_LOAD, + mmu_idx, false, true); + + if (vbase_ret != TRANSLATE_SUCCESS) { + return vbase_ret; + } pte_addr = vbase + idx * ptesize; } else { diff --git a/target/riscv/helper.h b/target/riscv/helper.h index debb22a480..b36be978d5 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -76,3 +76,8 @@ DEF_HELPER_2(mret, tl, env, tl) DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(tlb_flush, void, env) #endif + +/* Hypervisor functions */ +#ifndef CONFIG_USER_ONLY +DEF_HELPER_1(hyp_tlb_flush, void, env) +#endif diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index b883672e63..4c8d1215ce 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -64,7 +64,7 @@ @r2 ....... ..... ..... ... ..... ....... %rs1 %rd @hfence_gvma ....... ..... ..... ... ..... ....... %rs2 %rs1 -@hfence_bvma ....... ..... ..... ... ..... ....... %rs2 %rs1 +@hfence_vvma ....... ..... ..... ... ..... ....... %rs2 %rs1 @sfence_vma ....... ..... ..... ... ..... ....... %rs2 %rs1 @sfence_vm ....... ..... ..... ... ..... ....... %rs1 @@ -77,8 +77,6 @@ uret 0000000 00010 00000 000 00000 1110011 sret 0001000 00010 00000 000 00000 1110011 mret 0011000 00010 00000 000 00000 1110011 wfi 0001000 00101 00000 000 00000 1110011 -hfence_gvma 0110001 ..... ..... 000 00000 1110011 @hfence_gvma -hfence_bvma 0010001 ..... ..... 000 00000 1110011 @hfence_bvma sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm @@ -207,3 +205,7 @@ fcvt_w_d 1100001 00000 ..... ... ..... 1010011 @r2_rm fcvt_wu_d 1100001 00001 ..... ... ..... 1010011 @r2_rm fcvt_d_w 1101001 00000 ..... ... ..... 1010011 @r2_rm fcvt_d_wu 1101001 00001 ..... ... ..... 1010011 @r2_rm + +# *** RV32H Base Instruction Set *** +hfence_gvma 0110001 ..... ..... 000 00000 1110011 @hfence_gvma +hfence_vvma 0010001 ..... ..... 000 00000 1110011 @hfence_vvma diff --git a/target/riscv/insn_trans/trans_privileged.inc.c b/target/riscv/insn_trans/trans_privileged.inc.c index 5f26e0f5ea..2a61a853bf 100644 --- a/target/riscv/insn_trans/trans_privileged.inc.c +++ b/target/riscv/insn_trans/trans_privileged.inc.c @@ -95,41 +95,3 @@ static bool trans_sfence_vm(DisasContext *ctx, arg_sfence_vm *a) { return false; } - -static bool trans_hfence_gvma(DisasContext *ctx, arg_sfence_vma *a) -{ -#ifndef CONFIG_USER_ONLY - if (has_ext(ctx, RVH)) { - /* Hpervisor extensions exist */ - /* - * if (env->priv == PRV_M || - * (env->priv == PRV_S && - * !riscv_cpu_virt_enabled(env) && - * get_field(ctx->mstatus_fs, MSTATUS_TVM))) { - */ - gen_helper_tlb_flush(cpu_env); - return true; - /* } */ - } -#endif - return false; -} - -static bool trans_hfence_bvma(DisasContext *ctx, arg_sfence_vma *a) -{ -#ifndef CONFIG_USER_ONLY - if (has_ext(ctx, RVH)) { - /* Hpervisor extensions exist */ - /* - * if (env->priv == PRV_M || - * (env->priv == PRV_S && - * !riscv_cpu_virt_enabled(env) && - * get_field(ctx->mstatus_fs, MSTATUS_TVM))) { - */ - gen_helper_tlb_flush(cpu_env); - return true; - /* } */ - } -#endif - return false; -} diff --git a/target/riscv/insn_trans/trans_rvf.inc.c b/target/riscv/insn_trans/trans_rvf.inc.c index e23cd639a6..3bfd8881e7 100644 --- a/target/riscv/insn_trans/trans_rvf.inc.c +++ b/target/riscv/insn_trans/trans_rvf.inc.c @@ -23,6 +23,20 @@ return false; \ } while (0) +/* + * RISC-V requires NaN-boxing of narrower width floating + * point values. This applies when a 32-bit value is + * assigned to a 64-bit FP register. Thus this does not + * apply when the RVD extension is not present. + */ +static void gen_nanbox_fpr(DisasContext *ctx, int regno) +{ + if (has_ext(ctx, RVD)) { + tcg_gen_ori_i64(cpu_fpr[regno], cpu_fpr[regno], + MAKE_64BIT_MASK(32, 32)); + } +} + static bool trans_flw(DisasContext *ctx, arg_flw *a) { TCGv t0 = tcg_temp_new(); @@ -32,8 +46,7 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) tcg_gen_addi_tl(t0, t0, a->imm); tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], t0, ctx->mem_idx, MO_TEUL); - /* RISC-V requires NaN-boxing of narrower width floating point values */ - tcg_gen_ori_i64(cpu_fpr[a->rd], cpu_fpr[a->rd], 0xffffffff00000000ULL); + gen_nanbox_fpr(ctx, a->rd); tcg_temp_free(t0); mark_fs_dirty(ctx); diff --git a/target/riscv/insn_trans/trans_rvh.inc.c b/target/riscv/insn_trans/trans_rvh.inc.c new file mode 100644 index 0000000000..263b652d90 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvh.inc.c @@ -0,0 +1,37 @@ +/* + * RISC-V translation routines for the RVXI Base Integer Instruction Set. + * + * Copyright (c) 2020 Western Digital + * + * 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 <http://www.gnu.org/licenses/>. + */ + +static bool trans_hfence_gvma(DisasContext *ctx, arg_sfence_vma *a) +{ + REQUIRE_EXT(ctx, RVH); +#ifndef CONFIG_USER_ONLY + gen_helper_hyp_tlb_flush(cpu_env); + return true; +#endif + return false; +} + +static bool trans_hfence_vvma(DisasContext *ctx, arg_sfence_vma *a) +{ + REQUIRE_EXT(ctx, RVH); +#ifndef CONFIG_USER_ONLY + gen_helper_hyp_tlb_flush(cpu_env); + return true; +#endif + return false; +} diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index b0c49efc4a..7cccd42a1e 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -194,4 +194,17 @@ void helper_tlb_flush(CPURISCVState *env) } } +void helper_hyp_tlb_flush(CPURISCVState *env) +{ + CPUState *cs = env_cpu(env); + + if (env->priv == PRV_M || + (env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) { + tlb_flush(cs); + return; + } + + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); +} + #endif /* !CONFIG_USER_ONLY */ diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 0e6b640fbd..9418660f1b 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -233,12 +233,16 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, return true; } - /* - * if size is unknown (0), assume that all bytes - * from addr to the end of the page will be accessed. - */ if (size == 0) { - pmp_size = -(addr | TARGET_PAGE_MASK); + if (riscv_feature(env, RISCV_FEATURE_MMU)) { + /* + * If size is unknown (0), assume that all bytes + * from addr to the end of the page will be accessed. + */ + pmp_size = -(addr | TARGET_PAGE_MASK); + } else { + pmp_size = sizeof(target_ulong); + } } else { pmp_size = size; } diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 43bf7e39a6..ce71ca7a92 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -711,6 +711,7 @@ static bool gen_shift(DisasContext *ctx, arg_r *a, #include "insn_trans/trans_rva.inc.c" #include "insn_trans/trans_rvf.inc.c" #include "insn_trans/trans_rvd.inc.c" +#include "insn_trans/trans_rvh.inc.c" #include "insn_trans/trans_privileged.inc.c" /* Include the auto-generated decoder for 16 bit insn */ diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index 7a14c52c12..a412926d27 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -292,8 +292,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, sch = css_find_subch(m, cssid, ssid, schid); if (sch) { if (css_subch_visible(sch)) { - css_do_stsch(sch, &schib); - cc = 0; + cc = css_do_stsch(sch, &schib); } else { /* Indicate no more subchannels in this css/ss */ cc = 3; diff --git a/tests/acceptance/machine_rx_gdbsim.py b/tests/acceptance/machine_rx_gdbsim.py new file mode 100644 index 0000000000..a44f2c87da --- /dev/null +++ b/tests/acceptance/machine_rx_gdbsim.py @@ -0,0 +1,68 @@ +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa <crosa@redhat.com> +# +# 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 exec_command_and_wait_for_pattern +from avocado_qemu import wait_for_console_pattern +from avocado.utils import archive + + +class RxGdbSimMachine(Test): + + timeout = 30 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + def test_uboot(self): + """ + U-Boot and checks that the console is operational. + + :avocado: tags=arch:rx + :avocado: tags=machine:gdbsim-r5f562n8 + :avocado: tags=endian:little + """ + uboot_url = ('https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz') + uboot_hash = '9b78dbd43b40b2526848c0b1ce9de02c24f4dcdb' + uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) + uboot_path = archive.uncompress(uboot_path, self.workdir) + + self.vm.set_console() + self.vm.add_args('-bios', uboot_path, + '-no-reboot') + self.vm.launch() + uboot_version = 'U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty' + wait_for_console_pattern(self, uboot_version) + gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)' + # FIXME limit baudrate on chardev, else we type too fast + #exec_command_and_wait_for_pattern(self, 'version', gcc_version) + + def test_linux_sash(self): + """ + Boots a Linux kernel and checks that the console is operational. + + :avocado: tags=arch:rx + :avocado: tags=machine:gdbsim-r5f562n7 + :avocado: tags=endian:little + """ + dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-qemu.dtb') + dtb_hash = '7b4e4e2c71905da44e86ce47adee2210b026ac18' + dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash) + kernel_url = ('http://acc.dl.osdn.jp/users/23/23845/zImage') + kernel_hash = '39a81067f8d72faad90866ddfefa19165d68fc99' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon' + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-no-reboot') + self.vm.launch() + wait_for_console_pattern(self, 'Sash command shell (version 1.1.1)', + failure_message='Kernel panic - not syncing') + exec_command_and_wait_for_pattern(self, 'printenv', 'TERM=linux') diff --git a/tests/acceptance/replay_kernel.py b/tests/acceptance/replay_kernel.py new file mode 100644 index 0000000000..60621417dd --- /dev/null +++ b/tests/acceptance/replay_kernel.py @@ -0,0 +1,300 @@ +# Record/replay test that boots a Linux kernel +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import logging +import time + +from avocado import skipIf +from avocado_qemu import wait_for_console_pattern +from avocado.utils import archive +from avocado.utils import process +from boot_linux_console import LinuxKernelTest + +class ReplayKernel(LinuxKernelTest): + """ + Boots a Linux kernel in record mode and checks that the console + is operational and the kernel command line is properly passed + from QEMU to the kernel. + Then replays the same scenario and verifies, that QEMU correctly + terminates. + """ + + timeout = 90 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 ' + + def run_vm(self, kernel_path, kernel_command_line, console_pattern, + record, shift, args, replay_path): + logger = logging.getLogger('replay') + start_time = time.time() + vm = self.get_vm() + vm.set_console() + if record: + logger.info('recording the execution...') + mode = 'record' + else: + logger.info('replaying the execution...') + mode = 'replay' + vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % + (shift, mode, replay_path), + '-kernel', kernel_path, + '-append', kernel_command_line, + '-net', 'none', + '-no-reboot') + if args: + vm.add_args(*args) + vm.launch() + self.wait_for_console_pattern(console_pattern, vm) + if record: + vm.shutdown() + logger.info('finished the recording with log size %s bytes' + % os.path.getsize(replay_path)) + else: + vm.wait() + logger.info('successfully finished the replay') + elapsed = time.time() - start_time + logger.info('elapsed time %.2f sec' % elapsed) + return elapsed + + def run_rr(self, kernel_path, kernel_command_line, console_pattern, + shift=7, args=None): + replay_path = os.path.join(self.workdir, 'replay.bin') + t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern, + True, shift, args, replay_path) + t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, + False, shift, args, replay_path) + logger = logging.getLogger('replay') + logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) + + @skipIf(os.getenv('CONTINUOUS_INTEGRATION'), 'Running on Travis-CI') + def test_x86_64_pc(self): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:pc + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' + '/linux/releases/29/Everything/x86_64/os/images/pxeboot' + '/vmlinuz') + kernel_hash = '23bebd2680757891cf7adedb033532163a792495' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'VFS: Cannot open root device' + + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + def test_aarch64_virt(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:virt + :avocado: tags=cpu:cortex-a53 + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' + '/linux/releases/29/Everything/aarch64/os/images/pxeboot' + '/vmlinuz') + kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'VFS: Cannot open root device' + + self.run_rr(kernel_path, kernel_command_line, console_pattern, + args=('-cpu', 'cortex-a53')) + + def test_arm_virt(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:virt + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' + '/linux/releases/29/Everything/armhfp/os/images/pxeboot' + '/vmlinuz') + kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'VFS: Cannot open root device' + + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) + + def test_arm_cubieboard_initrd(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:cubieboard + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + initrd_url = ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv5.cpio.gz') + initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' + initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.gzip_uncompress(initrd_path_gz, initrd_path) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, + args=('-dtb', dtb_path, + '-initrd', initrd_path, + '-no-reboot')) + + def test_ppc64_pseries(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:pseries + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/29/Everything/ppc64le/os' + '/ppc/ppc64/vmlinuz') + kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' + # icount is not good enough for PPC64 for complete boot yet + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + def test_m68k_q800(self): + """ + :avocado: tags=arch:m68k + :avocado: tags=machine:q800 + """ + deb_url = ('https://snapshot.debian.org/archive/debian-ports' + '/20191021T083923Z/pool-m68k/main' + '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb') + deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinux-5.3.0-1-m68k') + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 vga=off') + console_pattern = 'No filesystem could mount root' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + def do_test_advcal_2018(self, file_path, kernel_name, args=None): + archive.extract(file_path, self.workdir) + + for entry in os.scandir(self.workdir): + if entry.name.startswith('day') and entry.is_dir(): + kernel_path = os.path.join(entry.path, kernel_name) + break + + kernel_command_line = '' + console_pattern = 'QEMU advent calendar' + self.run_rr(kernel_path, kernel_command_line, console_pattern, + args=args) + + def test_arm_vexpressa9(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:vexpress-a9 + """ + tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day16.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + dtb_path = self.workdir + '/day16/vexpress-v2p-ca9.dtb' + self.do_test_advcal_2018(file_path, 'winter.zImage', + args=('-dtb', dtb_path)) + + def test_m68k_mcf5208evb(self): + """ + :avocado: tags=arch:m68k + :avocado: tags=machine:mcf5208evb + """ + tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day07.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + self.do_test_advcal_2018(file_path, 'sanity-clause.elf') + + def test_microblaze_s3adsp1800(self): + """ + :avocado: tags=arch:microblaze + :avocado: tags=machine:petalogix-s3adsp1800 + """ + tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day17.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + self.do_test_advcal_2018(file_path, 'ballerina.bin') + + def test_ppc64_e500(self): + """ + :avocado: tags=arch:ppc64 + :avocado: tags=machine:ppce500 + :avocado: tags=cpu:e5500 + """ + tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day19.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + self.do_test_advcal_2018(file_path, 'uImage', ('-cpu', 'e5500')) + + def test_ppc_g3beige(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:g3beige + """ + tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day15.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + self.do_test_advcal_2018(file_path, 'invaders.elf', + args=('-M', 'graphics=off')) + + def test_ppc_mac99(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:mac99 + """ + tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day15.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + self.do_test_advcal_2018(file_path, 'invaders.elf', + args=('-M', 'graphics=off')) + + def test_sparc_ss20(self): + """ + :avocado: tags=arch:sparc + :avocado: tags=machine:SS-20 + """ + tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day11.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + self.do_test_advcal_2018(file_path, 'zImage.elf') + + def test_xtensa_lx60(self): + """ + :avocado: tags=arch:xtensa + :avocado: tags=machine:lx60 + :avocado: tags=cpu:dc233c + """ + tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' + tar_url = ('https://www.qemu-advent-calendar.org' + '/2018/download/day02.tar.xz') + file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) + self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf', + args=('-cpu', 'dc233c')) diff --git a/tests/qht-bench.c b/tests/qht-bench.c index e3b512f26f..eb88a90137 100644 --- a/tests/qht-bench.c +++ b/tests/qht-bench.c @@ -284,7 +284,8 @@ static void do_threshold(double rate, uint64_t *threshold) if (rate == 1.0) { *threshold = UINT64_MAX; } else { - *threshold = rate * UINT64_MAX; + *threshold = (rate * 0xffff000000000000ull) + + (rate * 0x0000ffffffffffffull); } } diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 4692173676..f7e062c189 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -159,16 +159,35 @@ static bool resp_get_feature(QDict *resp, const char *feature) qobject_unref(_resp); \ }) -#define assert_feature(qts, cpu_type, feature, expected_value) \ +#define resp_assert_feature(resp, feature, expected_value) \ ({ \ - QDict *_resp, *_props; \ + QDict *_props; \ \ - _resp = do_query_no_props(qts, cpu_type); \ g_assert(_resp); \ g_assert(resp_has_props(_resp)); \ _props = resp_get_props(_resp); \ g_assert(qdict_get(_props, feature)); \ g_assert(qdict_get_bool(_props, feature) == (expected_value)); \ +}) + +#define assert_feature(qts, cpu_type, feature, expected_value) \ +({ \ + QDict *_resp; \ + \ + _resp = do_query_no_props(qts, cpu_type); \ + g_assert(_resp); \ + resp_assert_feature(_resp, feature, expected_value); \ + qobject_unref(_resp); \ +}) + +#define assert_set_feature(qts, cpu_type, feature, value) \ +({ \ + const char *_fmt = (value) ? "{ %s: true }" : "{ %s: false }"; \ + QDict *_resp; \ + \ + _resp = do_query(qts, cpu_type, _fmt, feature); \ + g_assert(_resp); \ + resp_assert_feature(_resp, feature, value); \ qobject_unref(_resp); \ }) @@ -424,10 +443,14 @@ static void test_query_cpu_model_expansion(const void *data) assert_error(qts, "host", "The CPU type 'host' requires KVM", NULL); /* Test expected feature presence/absence for some cpu types */ - assert_has_feature_enabled(qts, "max", "pmu"); assert_has_feature_enabled(qts, "cortex-a15", "pmu"); assert_has_not_feature(qts, "cortex-a15", "aarch64"); + /* Enabling and disabling pmu should always work. */ + assert_has_feature_enabled(qts, "max", "pmu"); + assert_set_feature(qts, "max", "pmu", false); + assert_set_feature(qts, "max", "pmu", true); + assert_has_not_feature(qts, "max", "kvm-no-adjvtime"); if (g_str_equal(qtest_get_arch(), "aarch64")) { @@ -464,7 +487,10 @@ static void test_query_cpu_model_expansion_kvm(const void *data) 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); + assert_set_feature(qts, "host", "kvm-no-adjvtime", false); if (g_str_equal(qtest_get_arch(), "aarch64")) { bool kvm_supports_sve; @@ -475,7 +501,11 @@ static void test_query_cpu_model_expansion_kvm(const void *data) char *error; assert_has_feature_enabled(qts, "host", "aarch64"); + + /* Enabling and disabling pmu should always work. */ assert_has_feature_enabled(qts, "host", "pmu"); + assert_set_feature(qts, "host", "pmu", false); + assert_set_feature(qts, "host", "pmu", true); assert_error(qts, "cortex-a15", "We cannot guarantee the CPU type 'cortex-a15' works " diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 916f1be224..39ddc77c85 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -57,6 +57,10 @@ #include <lwp.h> #endif +#ifdef __APPLE__ +#include <mach-o/dyld.h> +#endif + #include "qemu/mmap-alloc.h" #ifdef CONFIG_DEBUG_STACK_USAGE @@ -375,6 +379,17 @@ void qemu_init_exec_dir(const char *argv0) p = buf; } } +#elif defined(__APPLE__) + { + char fpath[PATH_MAX]; + uint32_t len = sizeof(fpath); + if (_NSGetExecutablePath(fpath, &len) == 0) { + p = realpath(fpath, buf); + if (!p) { + return; + } + } + } #endif /* If we don't have any way of figuring out the actual executable location then try argv[0]. */ |