mirror of https://github.com/xemu-project/xemu.git
target-arm queue:
* raspi boards: some cleanup * raspi: implement the bcm2835 system timer device * raspi: implement a dummy thermal sensor * misc devices: switch to ptimer transaction API * cache TB flag state to improve performance of cpu_get_tb_cpu_state * aspeed: Add an AST2600 eval board -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl2y5m0ZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3hDGEACjvjKX9w02BEhcfnLlXuuN DM9AHZnprTQThsbA1KRR8UmHaY+Y4rTE9KqhLH9Ym0QIjO7CXlWniDYS5zA7aLuz Lo+6b39QZt0QZsTygWzzO3efHQvI0+z3wikk+cZm2RVeVs6dQggKjO7P3jzkp30Y ncMQVPT3jokN5++NpebqZk+6iesIH2nYxdjxwJNbgmEtU9MBk1C+6u8vo7vOJjqZ VJSA91esKkHgNSi+NQKSGk8fO+8cTw+7OqJS6ssH5K6/ndI59EgueKS4mt9WCKqb gfy+/mukKegVA4jd6NYNRsFKtOR1xqkwJViTpXiCb6bBMDS4J14x9KGaUISW0gMn urGrbvwq985HvZGIXqUH3GX7IwEHbFBgwaRl0aPmvJ+H24okEhSRWgw4V9ReLPIO GDoSr1gHHR88uo7v5F/Bu6OE3qRGqEvAG3ujiHHET9F3ilk1aUxgos41YkBriQbm pDwrP7kX1oUlEmeGmdRicxMFy304rlXtnT1qF6aMQz9k93R37p6Fr1q2jAFCsX/V p9udeX+fv1FIcEThHXIC6KEyGgldp38qRD5cTwcRpALyCvVBB3HLYEb7oIbfw7mn pUod2jZX2U1YAFWrXwc4DbRe6iDQs8wfKz/iuF44OW0Yy78iIaNqyhB70UA3F1V+ WxIJs1FUEylaaypzkPwVBA== =hcWd -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20191025' into staging target-arm queue: * raspi boards: some cleanup * raspi: implement the bcm2835 system timer device * raspi: implement a dummy thermal sensor * misc devices: switch to ptimer transaction API * cache TB flag state to improve performance of cpu_get_tb_cpu_state * aspeed: Add an AST2600 eval board # gpg: Signature made Fri 25 Oct 2019 13:11:25 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20191025: (42 commits) hw/arm/highbank: Use AddressSpace when using write_secondary_boot() hw/arm/raspi: Use AddressSpace when using arm_boot::write_secondary_boot hw/arm/bcm2836: Rename cpus[] as cpu[].core hw/arm/bcm2836: Make the SoC code modular hw/arm/bcm2835_peripherals: Use the SYS_timer hw/timer/bcm2835: Add the BCM2835 SYS_timer hw/arm/bcm2835_peripherals: Use the thermal sensor block hw/misc/bcm2835_thermal: Add a dummy BCM2835 thermal sensor hw/watchdog/milkymist-sysctl.c: Switch to transaction-based ptimer API hw/m68k/mcf5206.c: Switch to transaction-based ptimer API hw/timer/grlib_gptimer.c: Switch to transaction-based ptimer API hw/timer/slavio_timer.c: Switch to transaction-based ptimer API hw/timer/slavio_timer: Remove useless check for NULL t->timer hw/dma/xilinx_axidma.c: Switch to transaction-based ptimer API hw/timer/xilinx_timer.c: Switch to transaction-based ptimer API hw/net/fsl_etsec/etsec.c: Switch to transaction-based ptimer API target/arm: Rely on hflags correct in cpu_get_tb_cpu_state linux-user/arm: Rebuild hflags for TARGET_WORDS_BIGENDIAN linux-user/aarch64: Rebuild hflags for TARGET_WORDS_BIGENDIAN target/arm: Rebuild hflags for M-profile NVIC ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
7bc8f97342
|
@ -88,6 +88,10 @@ struct AspeedBoardState {
|
|||
/* Witherspoon hardware value: 0xF10AD216 (but use romulus definition) */
|
||||
#define WITHERSPOON_BMC_HW_STRAP1 ROMULUS_BMC_HW_STRAP1
|
||||
|
||||
/* AST2600 evb hardware value */
|
||||
#define AST2600_EVB_HW_STRAP1 0x000000C0
|
||||
#define AST2600_EVB_HW_STRAP2 0x00000003
|
||||
|
||||
/*
|
||||
* The max ram region is for firmwares that scan the address space
|
||||
* with load/store to guess how much RAM the SoC has.
|
||||
|
@ -187,6 +191,8 @@ static void aspeed_board_init(MachineState *machine,
|
|||
&error_abort);
|
||||
object_property_set_int(OBJECT(&bmc->soc), cfg->hw_strap1, "hw-strap1",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&bmc->soc), cfg->hw_strap2, "hw-strap2",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&bmc->soc), cfg->num_cs, "num-cs",
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&bmc->soc), machine->smp.cpus, "num-cpus",
|
||||
|
@ -308,6 +314,12 @@ static void ast2500_evb_i2c_init(AspeedBoardState *bmc)
|
|||
i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32);
|
||||
}
|
||||
|
||||
static void ast2600_evb_i2c_init(AspeedBoardState *bmc)
|
||||
{
|
||||
/* Start with some devices on our I2C busses */
|
||||
ast2500_evb_i2c_init(bmc);
|
||||
}
|
||||
|
||||
static void romulus_bmc_i2c_init(AspeedBoardState *bmc)
|
||||
{
|
||||
AspeedSoCState *soc = &bmc->soc;
|
||||
|
@ -455,6 +467,17 @@ static const AspeedBoardConfig aspeed_boards[] = {
|
|||
.num_cs = 2,
|
||||
.i2c_init = witherspoon_bmc_i2c_init,
|
||||
.ram = 512 * MiB,
|
||||
}, {
|
||||
.name = MACHINE_TYPE_NAME("ast2600-evb"),
|
||||
.desc = "Aspeed AST2600 EVB (Cortex A7)",
|
||||
.soc_name = "ast2600-a0",
|
||||
.hw_strap1 = AST2600_EVB_HW_STRAP1,
|
||||
.hw_strap2 = AST2600_EVB_HW_STRAP2,
|
||||
.fmc_model = "w25q512jv",
|
||||
.spi_model = "mx66u51235f",
|
||||
.num_cs = 1,
|
||||
.i2c_init = ast2600_evb_i2c_init,
|
||||
.ram = 1 * GiB,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,10 @@ static void bcm2835_peripherals_init(Object *obj)
|
|||
/* Interrupt Controller */
|
||||
sysbus_init_child_obj(obj, "ic", &s->ic, sizeof(s->ic), TYPE_BCM2835_IC);
|
||||
|
||||
/* SYS Timer */
|
||||
sysbus_init_child_obj(obj, "systimer", &s->systmr, sizeof(s->systmr),
|
||||
TYPE_BCM2835_SYSTIMER);
|
||||
|
||||
/* UART0 */
|
||||
sysbus_init_child_obj(obj, "uart0", &s->uart0, sizeof(s->uart0),
|
||||
TYPE_PL011);
|
||||
|
@ -111,6 +115,10 @@ static void bcm2835_peripherals_init(Object *obj)
|
|||
object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
|
||||
OBJECT(&s->gpu_bus_mr), &error_abort);
|
||||
|
||||
/* Thermal */
|
||||
sysbus_init_child_obj(obj, "thermal", &s->thermal, sizeof(s->thermal),
|
||||
TYPE_BCM2835_THERMAL);
|
||||
|
||||
/* GPIO */
|
||||
sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio),
|
||||
TYPE_BCM2835_GPIO);
|
||||
|
@ -167,6 +175,18 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
|
||||
sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
|
||||
|
||||
/* Sys Timer */
|
||||
object_property_set_bool(OBJECT(&s->systmr), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
memory_region_add_subregion(&s->peri_mr, ST_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systmr), 0));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->systmr), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
|
||||
INTERRUPT_ARM_TIMER));
|
||||
|
||||
/* UART0 */
|
||||
qdev_prop_set_chr(DEVICE(&s->uart0), "chardev", serial_hd(0));
|
||||
object_property_set_bool(OBJECT(&s->uart0), true, "realized", &err);
|
||||
|
@ -321,6 +341,15 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||
INTERRUPT_DMA0 + n));
|
||||
}
|
||||
|
||||
/* THERMAL */
|
||||
object_property_set_bool(OBJECT(&s->thermal), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
memory_region_add_subregion(&s->peri_mr, THERMAL_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->thermal), 0));
|
||||
|
||||
/* GPIO */
|
||||
object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err);
|
||||
if (err) {
|
||||
|
@ -339,7 +368,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
|
||||
create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40);
|
||||
create_unimp(s, &s->systmr, "bcm2835-systimer", ST_OFFSET, 0x20);
|
||||
create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x1000);
|
||||
create_unimp(s, &s->a2w, "bcm2835-a2w", A2W_OFFSET, 0x1000);
|
||||
create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100);
|
||||
|
|
|
@ -16,15 +16,11 @@
|
|||
#include "hw/arm/raspi_platform.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
/* Peripheral base address seen by the CPU */
|
||||
#define BCM2836_PERI_BASE 0x3F000000
|
||||
|
||||
/* "QA7" (Pi2) interrupt controller and mailboxes etc. */
|
||||
#define BCM2836_CONTROL_BASE 0x40000000
|
||||
|
||||
struct BCM283XInfo {
|
||||
const char *name;
|
||||
const char *cpu_type;
|
||||
hwaddr peri_base; /* Peripheral base address seen by the CPU */
|
||||
hwaddr ctrl_base; /* Interrupt controller and mailboxes etc. */
|
||||
int clusterid;
|
||||
};
|
||||
|
||||
|
@ -32,12 +28,16 @@ static const BCM283XInfo bcm283x_socs[] = {
|
|||
{
|
||||
.name = TYPE_BCM2836,
|
||||
.cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"),
|
||||
.peri_base = 0x3f000000,
|
||||
.ctrl_base = 0x40000000,
|
||||
.clusterid = 0xf,
|
||||
},
|
||||
#ifdef TARGET_AARCH64
|
||||
{
|
||||
.name = TYPE_BCM2837,
|
||||
.cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"),
|
||||
.peri_base = 0x3f000000,
|
||||
.ctrl_base = 0x40000000,
|
||||
.clusterid = 0x0,
|
||||
},
|
||||
#endif
|
||||
|
@ -51,8 +51,9 @@ static void bcm2836_init(Object *obj)
|
|||
int n;
|
||||
|
||||
for (n = 0; n < BCM283X_NCPUS; n++) {
|
||||
object_initialize_child(obj, "cpu[*]", &s->cpus[n], sizeof(s->cpus[n]),
|
||||
info->cpu_type, &error_abort, NULL);
|
||||
object_initialize_child(obj, "cpu[*]", &s->cpu[n].core,
|
||||
sizeof(s->cpu[n].core), info->cpu_type,
|
||||
&error_abort, NULL);
|
||||
}
|
||||
|
||||
sysbus_init_child_obj(obj, "control", &s->control, sizeof(s->control),
|
||||
|
@ -104,7 +105,7 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
|
||||
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0,
|
||||
BCM2836_PERI_BASE, 1);
|
||||
info->peri_base, 1);
|
||||
|
||||
/* bcm2836 interrupt controller (and mailboxes, etc.) */
|
||||
object_property_set_bool(OBJECT(&s->control), true, "realized", &err);
|
||||
|
@ -113,7 +114,7 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, BCM2836_CONTROL_BASE);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, info->ctrl_base);
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0));
|
||||
|
@ -122,11 +123,11 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
|
|||
|
||||
for (n = 0; n < BCM283X_NCPUS; n++) {
|
||||
/* TODO: this should be converted to a property of ARM_CPU */
|
||||
s->cpus[n].mp_affinity = (info->clusterid << 8) | n;
|
||||
s->cpu[n].core.mp_affinity = (info->clusterid << 8) | n;
|
||||
|
||||
/* set periphbase/CBAR value for CPU-local registers */
|
||||
object_property_set_int(OBJECT(&s->cpus[n]),
|
||||
BCM2836_PERI_BASE + MSYNC_OFFSET,
|
||||
object_property_set_int(OBJECT(&s->cpu[n].core),
|
||||
info->peri_base,
|
||||
"reset-cbar", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
|
@ -134,14 +135,15 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
|
||||
/* start powered off if not enabled */
|
||||
object_property_set_bool(OBJECT(&s->cpus[n]), n >= s->enabled_cpus,
|
||||
object_property_set_bool(OBJECT(&s->cpu[n].core), n >= s->enabled_cpus,
|
||||
"start-powered-off", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpus[n]), true, "realized", &err);
|
||||
object_property_set_bool(OBJECT(&s->cpu[n].core), true,
|
||||
"realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
|
@ -149,18 +151,18 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
|
|||
|
||||
/* Connect irq/fiq outputs from the interrupt controller. */
|
||||
qdev_connect_gpio_out_named(DEVICE(&s->control), "irq", n,
|
||||
qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_IRQ));
|
||||
qdev_get_gpio_in(DEVICE(&s->cpu[n].core), ARM_CPU_IRQ));
|
||||
qdev_connect_gpio_out_named(DEVICE(&s->control), "fiq", n,
|
||||
qdev_get_gpio_in(DEVICE(&s->cpus[n]), ARM_CPU_FIQ));
|
||||
qdev_get_gpio_in(DEVICE(&s->cpu[n].core), ARM_CPU_FIQ));
|
||||
|
||||
/* Connect timers from the CPU to the interrupt controller */
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_PHYS,
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_PHYS,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->control), "cntpnsirq", n));
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_VIRT,
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_VIRT,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->control), "cntvirq", n));
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_HYP,
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_HYP,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->control), "cnthpirq", n));
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpus[n]), GTIMER_SEC,
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpu[n].core), GTIMER_SEC,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->control), "cntpsirq", n));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,8 @@ static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
|
|||
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
|
||||
smpboot[n] = tswap32(smpboot[n]);
|
||||
}
|
||||
rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), SMP_BOOT_ADDR);
|
||||
rom_add_blob_fixed_as("smpboot", smpboot, sizeof(smpboot), SMP_BOOT_ADDR,
|
||||
arm_boot_address_space(cpu, info));
|
||||
}
|
||||
|
||||
static void hb_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
|
||||
|
|
|
@ -60,12 +60,14 @@ static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info)
|
|||
QEMU_BUILD_BUG_ON((BOARDSETUP_ADDR & 0xf) != 0
|
||||
|| (BOARDSETUP_ADDR >> 4) >= 0x100);
|
||||
|
||||
rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
|
||||
info->smp_loader_start);
|
||||
rom_add_blob_fixed_as("raspi_smpboot", smpboot, sizeof(smpboot),
|
||||
info->smp_loader_start,
|
||||
arm_boot_address_space(cpu, info));
|
||||
}
|
||||
|
||||
static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info)
|
||||
{
|
||||
AddressSpace *as = arm_boot_address_space(cpu, info);
|
||||
/* Unlike the AArch32 version we don't need to call the board setup hook.
|
||||
* The mechanism for doing the spin-table is also entirely different.
|
||||
* We must have four 64-bit fields at absolute addresses
|
||||
|
@ -92,10 +94,10 @@ static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info)
|
|||
0, 0, 0, 0
|
||||
};
|
||||
|
||||
rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
|
||||
info->smp_loader_start);
|
||||
rom_add_blob_fixed("raspi_spintables", spintables, sizeof(spintables),
|
||||
SPINTABLE_ADDR);
|
||||
rom_add_blob_fixed_as("raspi_smpboot", smpboot, sizeof(smpboot),
|
||||
info->smp_loader_start, as);
|
||||
rom_add_blob_fixed_as("raspi_spintables", spintables, sizeof(spintables),
|
||||
SPINTABLE_ADDR, as);
|
||||
}
|
||||
|
||||
static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "hw/ptimer.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#include "hw/stream.h"
|
||||
|
@ -104,7 +103,6 @@ enum {
|
|||
};
|
||||
|
||||
struct Stream {
|
||||
QEMUBH *bh;
|
||||
ptimer_state *ptimer;
|
||||
qemu_irq irq;
|
||||
|
||||
|
@ -242,6 +240,7 @@ static void stream_complete(struct Stream *s)
|
|||
unsigned int comp_delay;
|
||||
|
||||
/* Start the delayed timer. */
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
comp_delay = s->regs[R_DMACR] >> 24;
|
||||
if (comp_delay) {
|
||||
ptimer_stop(s->ptimer);
|
||||
|
@ -255,6 +254,7 @@ static void stream_complete(struct Stream *s)
|
|||
s->regs[R_DMASR] |= DMASR_IOC_IRQ;
|
||||
stream_reload_complete_cnt(s);
|
||||
}
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
}
|
||||
|
||||
static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev,
|
||||
|
@ -551,9 +551,10 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp)
|
|||
struct Stream *st = &s->streams[i];
|
||||
|
||||
st->nr = i;
|
||||
st->bh = qemu_bh_new(timer_hit, st);
|
||||
st->ptimer = ptimer_init_with_bh(st->bh, PTIMER_POLICY_DEFAULT);
|
||||
st->ptimer = ptimer_init(timer_hit, st, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_transaction_begin(st->ptimer);
|
||||
ptimer_set_freq(st->ptimer, s->freqhz);
|
||||
ptimer_transaction_commit(st->ptimer);
|
||||
}
|
||||
return;
|
||||
|
||||
|
|
|
@ -733,13 +733,13 @@ static void aspeed_gpio_get_pin(Object *obj, Visitor *v, const char *name,
|
|||
{
|
||||
int pin = 0xfff;
|
||||
bool level = true;
|
||||
char group[3];
|
||||
char group[4];
|
||||
AspeedGPIOState *s = ASPEED_GPIO(obj);
|
||||
int set_idx, group_idx = 0;
|
||||
|
||||
if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) {
|
||||
/* 1.8V gpio */
|
||||
if (sscanf(name, "gpio%3s%1d", group, &pin) != 2) {
|
||||
if (sscanf(name, "gpio%3[18A-E]%1d", group, &pin) != 2) {
|
||||
error_setg(errp, "%s: error reading %s", __func__, name);
|
||||
return;
|
||||
}
|
||||
|
@ -760,7 +760,7 @@ static void aspeed_gpio_set_pin(Object *obj, Visitor *v, const char *name,
|
|||
Error *local_err = NULL;
|
||||
bool level;
|
||||
int pin = 0xfff;
|
||||
char group[3];
|
||||
char group[4];
|
||||
AspeedGPIOState *s = ASPEED_GPIO(obj);
|
||||
int set_idx, group_idx = 0;
|
||||
|
||||
|
@ -771,7 +771,7 @@ static void aspeed_gpio_set_pin(Object *obj, Visitor *v, const char *name,
|
|||
}
|
||||
if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) {
|
||||
/* 1.8V gpio */
|
||||
if (sscanf(name, "gpio%3s%1d", group, &pin) != 2) {
|
||||
if (sscanf(name, "gpio%3[18A-E]%1d", group, &pin) != 2) {
|
||||
error_setg(errp, "%s: error reading %s", __func__, name);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2251,7 +2251,7 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
|
|||
}
|
||||
}
|
||||
nvic_irq_update(s);
|
||||
return MEMTX_OK;
|
||||
goto exit_ok;
|
||||
case 0x200 ... 0x23f: /* NVIC Set pend */
|
||||
/* the special logic in armv7m_nvic_set_pending()
|
||||
* is not needed since IRQs are never escalated
|
||||
|
@ -2269,9 +2269,9 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
|
|||
}
|
||||
}
|
||||
nvic_irq_update(s);
|
||||
return MEMTX_OK;
|
||||
goto exit_ok;
|
||||
case 0x300 ... 0x33f: /* NVIC Active */
|
||||
return MEMTX_OK; /* R/O */
|
||||
goto exit_ok; /* R/O */
|
||||
case 0x400 ... 0x5ef: /* NVIC Priority */
|
||||
startvec = (offset - 0x400) + NVIC_FIRST_IRQ; /* vector # */
|
||||
|
||||
|
@ -2281,10 +2281,10 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
|
|||
}
|
||||
}
|
||||
nvic_irq_update(s);
|
||||
return MEMTX_OK;
|
||||
goto exit_ok;
|
||||
case 0xd18 ... 0xd1b: /* System Handler Priority (SHPR1) */
|
||||
if (!arm_feature(&s->cpu->env, ARM_FEATURE_M_MAIN)) {
|
||||
return MEMTX_OK;
|
||||
goto exit_ok;
|
||||
}
|
||||
/* fall through */
|
||||
case 0xd1c ... 0xd23: /* System Handler Priority (SHPR2, SHPR3) */
|
||||
|
@ -2299,10 +2299,10 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
|
|||
set_prio(s, hdlidx, sbank, newprio);
|
||||
}
|
||||
nvic_irq_update(s);
|
||||
return MEMTX_OK;
|
||||
goto exit_ok;
|
||||
case 0xd28 ... 0xd2b: /* Configurable Fault Status (CFSR) */
|
||||
if (!arm_feature(&s->cpu->env, ARM_FEATURE_M_MAIN)) {
|
||||
return MEMTX_OK;
|
||||
goto exit_ok;
|
||||
}
|
||||
/* All bits are W1C, so construct 32 bit value with 0s in
|
||||
* the parts not written by the access size
|
||||
|
@ -2322,15 +2322,19 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
|
|||
*/
|
||||
s->cpu->env.v7m.cfsr[M_REG_NS] &= ~(value & R_V7M_CFSR_BFSR_MASK);
|
||||
}
|
||||
return MEMTX_OK;
|
||||
goto exit_ok;
|
||||
}
|
||||
if (size == 4) {
|
||||
nvic_writel(s, offset, value, attrs);
|
||||
return MEMTX_OK;
|
||||
goto exit_ok;
|
||||
}
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"NVIC: Bad write of size %d at offset 0x%x\n", size, offset);
|
||||
/* This is UNPREDICTABLE; treat as RAZ/WI */
|
||||
|
||||
exit_ok:
|
||||
/* Ensure any changes made are reflected in the cached hflags. */
|
||||
arm_rebuild_hflags(&s->cpu->env);
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
|
@ -57,10 +56,12 @@ static void m5206_timer_recalibrate(m5206_timer_state *s)
|
|||
int prescale;
|
||||
int mode;
|
||||
|
||||
ptimer_transaction_begin(s->timer);
|
||||
ptimer_stop(s->timer);
|
||||
|
||||
if ((s->tmr & TMR_RST) == 0)
|
||||
return;
|
||||
if ((s->tmr & TMR_RST) == 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
prescale = (s->tmr >> 8) + 1;
|
||||
mode = (s->tmr >> 1) & 3;
|
||||
|
@ -78,6 +79,8 @@ static void m5206_timer_recalibrate(m5206_timer_state *s)
|
|||
ptimer_set_limit(s->timer, s->trr, 0);
|
||||
|
||||
ptimer_run(s->timer, 0);
|
||||
exit:
|
||||
ptimer_transaction_commit(s->timer);
|
||||
}
|
||||
|
||||
static void m5206_timer_trigger(void *opaque)
|
||||
|
@ -123,7 +126,9 @@ static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val)
|
|||
s->tcr = val;
|
||||
break;
|
||||
case 0xc:
|
||||
ptimer_transaction_begin(s->timer);
|
||||
ptimer_set_count(s->timer, val);
|
||||
ptimer_transaction_commit(s->timer);
|
||||
break;
|
||||
case 0x11:
|
||||
s->ter &= ~val;
|
||||
|
@ -137,11 +142,9 @@ static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val)
|
|||
static m5206_timer_state *m5206_timer_init(qemu_irq irq)
|
||||
{
|
||||
m5206_timer_state *s;
|
||||
QEMUBH *bh;
|
||||
|
||||
s = g_new0(m5206_timer_state, 1);
|
||||
bh = qemu_bh_new(m5206_timer_trigger, s);
|
||||
s->timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT);
|
||||
s->timer = ptimer_init(m5206_timer_trigger, s, PTIMER_POLICY_DEFAULT);
|
||||
s->irq = irq;
|
||||
m5206_timer_reset(s);
|
||||
return s;
|
||||
|
|
|
@ -53,6 +53,7 @@ common-obj-$(CONFIG_OMAP) += omap_tap.o
|
|||
common-obj-$(CONFIG_RASPI) += bcm2835_mbox.o
|
||||
common-obj-$(CONFIG_RASPI) += bcm2835_property.o
|
||||
common-obj-$(CONFIG_RASPI) += bcm2835_rng.o
|
||||
common-obj-$(CONFIG_RASPI) += bcm2835_thermal.o
|
||||
common-obj-$(CONFIG_SLAVIO) += slavio_misc.o
|
||||
common-obj-$(CONFIG_ZYNQ) += zynq_slcr.o
|
||||
common-obj-$(CONFIG_ZYNQ) += zynq-xadc.o
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* BCM2835 dummy thermal sensor
|
||||
*
|
||||
* Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/misc/bcm2835_thermal.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
REG32(CTL, 0)
|
||||
FIELD(CTL, POWER_DOWN, 0, 1)
|
||||
FIELD(CTL, RESET, 1, 1)
|
||||
FIELD(CTL, BANDGAP_CTRL, 2, 3)
|
||||
FIELD(CTL, INTERRUPT_ENABLE, 5, 1)
|
||||
FIELD(CTL, DIRECT, 6, 1)
|
||||
FIELD(CTL, INTERRUPT_CLEAR, 7, 1)
|
||||
FIELD(CTL, HOLD, 8, 10)
|
||||
FIELD(CTL, RESET_DELAY, 18, 8)
|
||||
FIELD(CTL, REGULATOR_ENABLE, 26, 1)
|
||||
|
||||
REG32(STAT, 4)
|
||||
FIELD(STAT, DATA, 0, 10)
|
||||
FIELD(STAT, VALID, 10, 1)
|
||||
FIELD(STAT, INTERRUPT, 11, 1)
|
||||
|
||||
#define THERMAL_OFFSET_C 412
|
||||
#define THERMAL_COEFF (-0.538f)
|
||||
|
||||
static uint16_t bcm2835_thermal_temp2adc(int temp_C)
|
||||
{
|
||||
return (temp_C - THERMAL_OFFSET_C) / THERMAL_COEFF;
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_thermal_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
Bcm2835ThermalState *s = BCM2835_THERMAL(opaque);
|
||||
uint32_t val = 0;
|
||||
|
||||
switch (addr) {
|
||||
case A_CTL:
|
||||
val = s->ctl;
|
||||
break;
|
||||
case A_STAT:
|
||||
/* Temperature is constantly 25°C. */
|
||||
val = FIELD_DP32(bcm2835_thermal_temp2adc(25), STAT, VALID, true);
|
||||
break;
|
||||
default:
|
||||
/* MemoryRegionOps are aligned, so this can not happen. */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void bcm2835_thermal_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
Bcm2835ThermalState *s = BCM2835_THERMAL(opaque);
|
||||
|
||||
switch (addr) {
|
||||
case A_CTL:
|
||||
s->ctl = value;
|
||||
break;
|
||||
case A_STAT:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: write 0x%" PRIx64
|
||||
" to 0x%" HWADDR_PRIx "\n",
|
||||
__func__, value, addr);
|
||||
break;
|
||||
default:
|
||||
/* MemoryRegionOps are aligned, so this can not happen. */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bcm2835_thermal_ops = {
|
||||
.read = bcm2835_thermal_read,
|
||||
.write = bcm2835_thermal_write,
|
||||
.impl.max_access_size = 4,
|
||||
.valid.min_access_size = 4,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void bcm2835_thermal_reset(DeviceState *dev)
|
||||
{
|
||||
Bcm2835ThermalState *s = BCM2835_THERMAL(dev);
|
||||
|
||||
s->ctl = 0;
|
||||
}
|
||||
|
||||
static void bcm2835_thermal_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Bcm2835ThermalState *s = BCM2835_THERMAL(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_thermal_ops,
|
||||
s, TYPE_BCM2835_THERMAL, 8);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
|
||||
}
|
||||
|
||||
static const VMStateDescription bcm2835_thermal_vmstate = {
|
||||
.name = "bcm2835_thermal",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(ctl, Bcm2835ThermalState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void bcm2835_thermal_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = bcm2835_thermal_realize;
|
||||
dc->reset = bcm2835_thermal_reset;
|
||||
dc->vmsd = &bcm2835_thermal_vmstate;
|
||||
}
|
||||
|
||||
static const TypeInfo bcm2835_thermal_info = {
|
||||
.name = TYPE_BCM2835_THERMAL,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(Bcm2835ThermalState),
|
||||
.class_init = bcm2835_thermal_class_init,
|
||||
};
|
||||
|
||||
static void bcm2835_thermal_register_types(void)
|
||||
{
|
||||
type_register_static(&bcm2835_thermal_info);
|
||||
}
|
||||
|
||||
type_init(bcm2835_thermal_register_types)
|
|
@ -34,7 +34,6 @@
|
|||
#include "etsec.h"
|
||||
#include "registers.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
/* #define HEX_DUMP */
|
||||
|
@ -195,9 +194,11 @@ static void write_dmactrl(eTSEC *etsec,
|
|||
|
||||
if (!(value & DMACTRL_WOP)) {
|
||||
/* Start polling */
|
||||
ptimer_transaction_begin(etsec->ptimer);
|
||||
ptimer_stop(etsec->ptimer);
|
||||
ptimer_set_count(etsec->ptimer, 1);
|
||||
ptimer_run(etsec->ptimer, 1);
|
||||
ptimer_transaction_commit(etsec->ptimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,10 +392,10 @@ static void etsec_realize(DeviceState *dev, Error **errp)
|
|||
object_get_typename(OBJECT(dev)), dev->id, etsec);
|
||||
qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a);
|
||||
|
||||
|
||||
etsec->bh = qemu_bh_new(etsec_timer_hit, etsec);
|
||||
etsec->ptimer = ptimer_init_with_bh(etsec->bh, PTIMER_POLICY_DEFAULT);
|
||||
etsec->ptimer = ptimer_init(etsec_timer_hit, etsec, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_transaction_begin(etsec->ptimer);
|
||||
ptimer_set_freq(etsec->ptimer, 100);
|
||||
ptimer_transaction_commit(etsec->ptimer);
|
||||
}
|
||||
|
||||
static void etsec_instance_init(Object *obj)
|
||||
|
|
|
@ -141,7 +141,6 @@ typedef struct eTSEC {
|
|||
uint16_t phy_control;
|
||||
|
||||
/* Polling */
|
||||
QEMUBH *bh;
|
||||
struct ptimer_state *ptimer;
|
||||
|
||||
/* Whether we should flush the rx queue when buffer becomes available. */
|
||||
|
|
|
@ -47,3 +47,4 @@ common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
|
|||
common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
|
||||
common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o
|
||||
common-obj-$(CONFIG_MSF2) += mss-timer.o
|
||||
common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* BCM2835 SYS timer emulation
|
||||
*
|
||||
* Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Datasheet: BCM2835 ARM Peripherals (C6357-M-1398)
|
||||
* https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
|
||||
*
|
||||
* Only the free running 64-bit counter is implemented.
|
||||
* The 4 COMPARE registers and the interruption are not implemented.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/timer/bcm2835_systmr.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
REG32(CTRL_STATUS, 0x00)
|
||||
REG32(COUNTER_LOW, 0x04)
|
||||
REG32(COUNTER_HIGH, 0x08)
|
||||
REG32(COMPARE0, 0x0c)
|
||||
REG32(COMPARE1, 0x10)
|
||||
REG32(COMPARE2, 0x14)
|
||||
REG32(COMPARE3, 0x18)
|
||||
|
||||
static void bcm2835_systmr_update_irq(BCM2835SystemTimerState *s)
|
||||
{
|
||||
bool enable = !!s->reg.status;
|
||||
|
||||
trace_bcm2835_systmr_irq(enable);
|
||||
qemu_set_irq(s->irq, enable);
|
||||
}
|
||||
|
||||
static void bcm2835_systmr_update_compare(BCM2835SystemTimerState *s,
|
||||
unsigned timer_index)
|
||||
{
|
||||
/* TODO fow now, since neither Linux nor U-boot use these timers. */
|
||||
qemu_log_mask(LOG_UNIMP, "COMPARE register %u not implemented\n",
|
||||
timer_index);
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_systmr_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
|
||||
uint64_t r = 0;
|
||||
|
||||
switch (offset) {
|
||||
case A_CTRL_STATUS:
|
||||
r = s->reg.status;
|
||||
break;
|
||||
case A_COMPARE0 ... A_COMPARE3:
|
||||
r = s->reg.compare[(offset - A_COMPARE0) >> 2];
|
||||
break;
|
||||
case A_COUNTER_LOW:
|
||||
case A_COUNTER_HIGH:
|
||||
/* Free running counter at 1MHz */
|
||||
r = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
|
||||
r >>= 8 * (offset - A_COUNTER_LOW);
|
||||
r &= UINT32_MAX;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
trace_bcm2835_systmr_read(offset, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void bcm2835_systmr_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
BCM2835SystemTimerState *s = BCM2835_SYSTIMER(opaque);
|
||||
|
||||
trace_bcm2835_systmr_write(offset, value);
|
||||
switch (offset) {
|
||||
case A_CTRL_STATUS:
|
||||
s->reg.status &= ~value; /* Ack */
|
||||
bcm2835_systmr_update_irq(s);
|
||||
break;
|
||||
case A_COMPARE0 ... A_COMPARE3:
|
||||
s->reg.compare[(offset - A_COMPARE0) >> 2] = value;
|
||||
bcm2835_systmr_update_compare(s, (offset - A_COMPARE0) >> 2);
|
||||
break;
|
||||
case A_COUNTER_LOW:
|
||||
case A_COUNTER_HIGH:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only ofs 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bcm2835_systmr_ops = {
|
||||
.read = bcm2835_systmr_read,
|
||||
.write = bcm2835_systmr_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void bcm2835_systmr_reset(DeviceState *dev)
|
||||
{
|
||||
BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
|
||||
|
||||
memset(&s->reg, 0, sizeof(s->reg));
|
||||
}
|
||||
|
||||
static void bcm2835_systmr_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
BCM2835SystemTimerState *s = BCM2835_SYSTIMER(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_systmr_ops,
|
||||
s, "bcm2835-sys-timer", 0x20);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
|
||||
}
|
||||
|
||||
static const VMStateDescription bcm2835_systmr_vmstate = {
|
||||
.name = "bcm2835_sys_timer",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(reg.status, BCM2835SystemTimerState),
|
||||
VMSTATE_UINT32_ARRAY(reg.compare, BCM2835SystemTimerState, 4),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void bcm2835_systmr_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = bcm2835_systmr_realize;
|
||||
dc->reset = bcm2835_systmr_reset;
|
||||
dc->vmsd = &bcm2835_systmr_vmstate;
|
||||
}
|
||||
|
||||
static const TypeInfo bcm2835_systmr_info = {
|
||||
.name = TYPE_BCM2835_SYSTIMER,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(BCM2835SystemTimerState),
|
||||
.class_init = bcm2835_systmr_class_init,
|
||||
};
|
||||
|
||||
static void bcm2835_systmr_register_types(void)
|
||||
{
|
||||
type_register_static(&bcm2835_systmr_info);
|
||||
}
|
||||
|
||||
type_init(bcm2835_systmr_register_types);
|
|
@ -29,7 +29,6 @@
|
|||
#include "hw/irq.h"
|
||||
#include "hw/ptimer.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#include "trace.h"
|
||||
|
@ -63,7 +62,6 @@ typedef struct GPTimer GPTimer;
|
|||
typedef struct GPTimerUnit GPTimerUnit;
|
||||
|
||||
struct GPTimer {
|
||||
QEMUBH *bh;
|
||||
struct ptimer_state *ptimer;
|
||||
|
||||
qemu_irq irq;
|
||||
|
@ -93,6 +91,17 @@ struct GPTimerUnit {
|
|||
uint32_t config;
|
||||
};
|
||||
|
||||
static void grlib_gptimer_tx_begin(GPTimer *timer)
|
||||
{
|
||||
ptimer_transaction_begin(timer->ptimer);
|
||||
}
|
||||
|
||||
static void grlib_gptimer_tx_commit(GPTimer *timer)
|
||||
{
|
||||
ptimer_transaction_commit(timer->ptimer);
|
||||
}
|
||||
|
||||
/* Must be called within grlib_gptimer_tx_begin/commit block */
|
||||
static void grlib_gptimer_enable(GPTimer *timer)
|
||||
{
|
||||
assert(timer != NULL);
|
||||
|
@ -115,6 +124,7 @@ static void grlib_gptimer_enable(GPTimer *timer)
|
|||
ptimer_run(timer->ptimer, 1);
|
||||
}
|
||||
|
||||
/* Must be called within grlib_gptimer_tx_begin/commit block */
|
||||
static void grlib_gptimer_restart(GPTimer *timer)
|
||||
{
|
||||
assert(timer != NULL);
|
||||
|
@ -141,7 +151,9 @@ static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
|
|||
trace_grlib_gptimer_set_scaler(scaler, value);
|
||||
|
||||
for (i = 0; i < unit->nr_timers; i++) {
|
||||
ptimer_transaction_begin(unit->timers[i].ptimer);
|
||||
ptimer_set_freq(unit->timers[i].ptimer, value);
|
||||
ptimer_transaction_commit(unit->timers[i].ptimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,8 +278,10 @@ static void grlib_gptimer_write(void *opaque, hwaddr addr,
|
|||
switch (timer_addr) {
|
||||
case COUNTER_OFFSET:
|
||||
trace_grlib_gptimer_writel(id, addr, value);
|
||||
grlib_gptimer_tx_begin(&unit->timers[id]);
|
||||
unit->timers[id].counter = value;
|
||||
grlib_gptimer_enable(&unit->timers[id]);
|
||||
grlib_gptimer_tx_commit(&unit->timers[id]);
|
||||
return;
|
||||
|
||||
case COUNTER_RELOAD_OFFSET:
|
||||
|
@ -291,6 +305,7 @@ static void grlib_gptimer_write(void *opaque, hwaddr addr,
|
|||
/* gptimer_restart calls gptimer_enable, so if "enable" and "load"
|
||||
bits are present, we just have to call restart. */
|
||||
|
||||
grlib_gptimer_tx_begin(&unit->timers[id]);
|
||||
if (value & GPTIMER_LOAD) {
|
||||
grlib_gptimer_restart(&unit->timers[id]);
|
||||
} else if (value & GPTIMER_ENABLE) {
|
||||
|
@ -301,6 +316,7 @@ static void grlib_gptimer_write(void *opaque, hwaddr addr,
|
|||
value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
|
||||
|
||||
unit->timers[id].config = value;
|
||||
grlib_gptimer_tx_commit(&unit->timers[id]);
|
||||
return;
|
||||
|
||||
default:
|
||||
|
@ -344,9 +360,11 @@ static void grlib_gptimer_reset(DeviceState *d)
|
|||
timer->counter = 0;
|
||||
timer->reload = 0;
|
||||
timer->config = 0;
|
||||
ptimer_transaction_begin(timer->ptimer);
|
||||
ptimer_stop(timer->ptimer);
|
||||
ptimer_set_count(timer->ptimer, 0);
|
||||
ptimer_set_freq(timer->ptimer, unit->freq_hz);
|
||||
ptimer_transaction_commit(timer->ptimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,14 +383,16 @@ static void grlib_gptimer_realize(DeviceState *dev, Error **errp)
|
|||
GPTimer *timer = &unit->timers[i];
|
||||
|
||||
timer->unit = unit;
|
||||
timer->bh = qemu_bh_new(grlib_gptimer_hit, timer);
|
||||
timer->ptimer = ptimer_init_with_bh(timer->bh, PTIMER_POLICY_DEFAULT);
|
||||
timer->ptimer = ptimer_init(grlib_gptimer_hit, timer,
|
||||
PTIMER_POLICY_DEFAULT);
|
||||
timer->id = i;
|
||||
|
||||
/* One IRQ line for each timer */
|
||||
sysbus_init_irq(sbd, &timer->irq);
|
||||
|
||||
ptimer_transaction_begin(timer->ptimer);
|
||||
ptimer_set_freq(timer->ptimer, unit->freq_hz);
|
||||
ptimer_transaction_commit(timer->ptimer);
|
||||
}
|
||||
|
||||
memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops,
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "hw/ptimer.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
enum {
|
||||
|
@ -71,8 +70,6 @@ struct MilkymistSysctlState {
|
|||
|
||||
MemoryRegion regs_region;
|
||||
|
||||
QEMUBH *bh0;
|
||||
QEMUBH *bh1;
|
||||
ptimer_state *ptimer0;
|
||||
ptimer_state *ptimer1;
|
||||
|
||||
|
@ -161,14 +158,19 @@ static void sysctl_write(void *opaque, hwaddr addr, uint64_t value,
|
|||
s->regs[addr] = value;
|
||||
break;
|
||||
case R_TIMER0_COMPARE:
|
||||
ptimer_transaction_begin(s->ptimer0);
|
||||
ptimer_set_limit(s->ptimer0, value, 0);
|
||||
s->regs[addr] = value;
|
||||
ptimer_transaction_commit(s->ptimer0);
|
||||
break;
|
||||
case R_TIMER1_COMPARE:
|
||||
ptimer_transaction_begin(s->ptimer1);
|
||||
ptimer_set_limit(s->ptimer1, value, 0);
|
||||
s->regs[addr] = value;
|
||||
ptimer_transaction_commit(s->ptimer1);
|
||||
break;
|
||||
case R_TIMER0_CONTROL:
|
||||
ptimer_transaction_begin(s->ptimer0);
|
||||
s->regs[addr] = value;
|
||||
if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) {
|
||||
trace_milkymist_sysctl_start_timer0();
|
||||
|
@ -179,8 +181,10 @@ static void sysctl_write(void *opaque, hwaddr addr, uint64_t value,
|
|||
trace_milkymist_sysctl_stop_timer0();
|
||||
ptimer_stop(s->ptimer0);
|
||||
}
|
||||
ptimer_transaction_commit(s->ptimer0);
|
||||
break;
|
||||
case R_TIMER1_CONTROL:
|
||||
ptimer_transaction_begin(s->ptimer1);
|
||||
s->regs[addr] = value;
|
||||
if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) {
|
||||
trace_milkymist_sysctl_start_timer1();
|
||||
|
@ -191,6 +195,7 @@ static void sysctl_write(void *opaque, hwaddr addr, uint64_t value,
|
|||
trace_milkymist_sysctl_stop_timer1();
|
||||
ptimer_stop(s->ptimer1);
|
||||
}
|
||||
ptimer_transaction_commit(s->ptimer1);
|
||||
break;
|
||||
case R_ICAP:
|
||||
sysctl_icap_write(s, value);
|
||||
|
@ -263,8 +268,12 @@ static void milkymist_sysctl_reset(DeviceState *d)
|
|||
s->regs[i] = 0;
|
||||
}
|
||||
|
||||
ptimer_transaction_begin(s->ptimer0);
|
||||
ptimer_stop(s->ptimer0);
|
||||
ptimer_transaction_commit(s->ptimer0);
|
||||
ptimer_transaction_begin(s->ptimer1);
|
||||
ptimer_stop(s->ptimer1);
|
||||
ptimer_transaction_commit(s->ptimer1);
|
||||
|
||||
/* defaults */
|
||||
s->regs[R_ICAP] = ICAP_READY;
|
||||
|
@ -292,13 +301,15 @@ static void milkymist_sysctl_realize(DeviceState *dev, Error **errp)
|
|||
{
|
||||
MilkymistSysctlState *s = MILKYMIST_SYSCTL(dev);
|
||||
|
||||
s->bh0 = qemu_bh_new(timer0_hit, s);
|
||||
s->bh1 = qemu_bh_new(timer1_hit, s);
|
||||
s->ptimer0 = ptimer_init_with_bh(s->bh0, PTIMER_POLICY_DEFAULT);
|
||||
s->ptimer1 = ptimer_init_with_bh(s->bh1, PTIMER_POLICY_DEFAULT);
|
||||
s->ptimer0 = ptimer_init(timer0_hit, s, PTIMER_POLICY_DEFAULT);
|
||||
s->ptimer1 = ptimer_init(timer1_hit, s, PTIMER_POLICY_DEFAULT);
|
||||
|
||||
ptimer_transaction_begin(s->ptimer0);
|
||||
ptimer_set_freq(s->ptimer0, s->freq_hz);
|
||||
ptimer_transaction_commit(s->ptimer0);
|
||||
ptimer_transaction_begin(s->ptimer1);
|
||||
ptimer_set_freq(s->ptimer1, s->freq_hz);
|
||||
ptimer_transaction_commit(s->ptimer1);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_milkymist_sysctl = {
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "hw/sysbus.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
/*
|
||||
|
@ -213,6 +212,7 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
|
|||
saddr = addr >> 2;
|
||||
switch (saddr) {
|
||||
case TIMER_LIMIT:
|
||||
ptimer_transaction_begin(t->timer);
|
||||
if (slavio_timer_is_user(tc)) {
|
||||
uint64_t count;
|
||||
|
||||
|
@ -227,15 +227,14 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
|
|||
// set limit, reset counter
|
||||
qemu_irq_lower(t->irq);
|
||||
t->limit = val & TIMER_MAX_COUNT32;
|
||||
if (t->timer) {
|
||||
if (t->limit == 0) { /* free-run */
|
||||
ptimer_set_limit(t->timer,
|
||||
LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
|
||||
} else {
|
||||
ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1);
|
||||
}
|
||||
if (t->limit == 0) { /* free-run */
|
||||
ptimer_set_limit(t->timer,
|
||||
LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
|
||||
} else {
|
||||
ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1);
|
||||
}
|
||||
}
|
||||
ptimer_transaction_commit(t->timer);
|
||||
break;
|
||||
case TIMER_COUNTER:
|
||||
if (slavio_timer_is_user(tc)) {
|
||||
|
@ -247,7 +246,9 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
|
|||
t->reached = 0;
|
||||
count = ((uint64_t)t->counthigh) << 32 | t->count;
|
||||
trace_slavio_timer_mem_writel_limit(timer_index, count);
|
||||
ptimer_transaction_begin(t->timer);
|
||||
ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count));
|
||||
ptimer_transaction_commit(t->timer);
|
||||
} else {
|
||||
trace_slavio_timer_mem_writel_counter_invalid();
|
||||
}
|
||||
|
@ -255,13 +256,16 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
|
|||
case TIMER_COUNTER_NORST:
|
||||
// set limit without resetting counter
|
||||
t->limit = val & TIMER_MAX_COUNT32;
|
||||
ptimer_transaction_begin(t->timer);
|
||||
if (t->limit == 0) { /* free-run */
|
||||
ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0);
|
||||
} else {
|
||||
ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 0);
|
||||
}
|
||||
ptimer_transaction_commit(t->timer);
|
||||
break;
|
||||
case TIMER_STATUS:
|
||||
ptimer_transaction_begin(t->timer);
|
||||
if (slavio_timer_is_user(tc)) {
|
||||
// start/stop user counter
|
||||
if (val & 1) {
|
||||
|
@ -273,6 +277,7 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
|
|||
}
|
||||
}
|
||||
t->run = val & 1;
|
||||
ptimer_transaction_commit(t->timer);
|
||||
break;
|
||||
case TIMER_MODE:
|
||||
if (timer_index == 0) {
|
||||
|
@ -282,6 +287,7 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
|
|||
unsigned int processor = 1 << i;
|
||||
CPUTimerState *curr_timer = &s->cputimer[i + 1];
|
||||
|
||||
ptimer_transaction_begin(curr_timer->timer);
|
||||
// check for a change in timer mode for this processor
|
||||
if ((val & processor) != (s->cputimer_mode & processor)) {
|
||||
if (val & processor) { // counter -> user timer
|
||||
|
@ -308,6 +314,7 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
|
|||
trace_slavio_timer_mem_writel_mode_counter(timer_index);
|
||||
}
|
||||
}
|
||||
ptimer_transaction_commit(curr_timer->timer);
|
||||
}
|
||||
} else {
|
||||
trace_slavio_timer_mem_writel_mode_invalid();
|
||||
|
@ -367,10 +374,12 @@ static void slavio_timer_reset(DeviceState *d)
|
|||
curr_timer->count = 0;
|
||||
curr_timer->reached = 0;
|
||||
if (i <= s->num_cpus) {
|
||||
ptimer_transaction_begin(curr_timer->timer);
|
||||
ptimer_set_limit(curr_timer->timer,
|
||||
LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
|
||||
ptimer_run(curr_timer->timer, 0);
|
||||
curr_timer->run = 1;
|
||||
ptimer_transaction_commit(curr_timer->timer);
|
||||
}
|
||||
}
|
||||
s->cputimer_mode = 0;
|
||||
|
@ -380,7 +389,6 @@ static void slavio_timer_init(Object *obj)
|
|||
{
|
||||
SLAVIO_TIMERState *s = SLAVIO_TIMER(obj);
|
||||
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||
QEMUBH *bh;
|
||||
unsigned int i;
|
||||
TimerContext *tc;
|
||||
|
||||
|
@ -392,9 +400,11 @@ static void slavio_timer_init(Object *obj)
|
|||
tc->s = s;
|
||||
tc->timer_index = i;
|
||||
|
||||
bh = qemu_bh_new(slavio_timer_irq, tc);
|
||||
s->cputimer[i].timer = ptimer_init_with_bh(bh, PTIMER_POLICY_DEFAULT);
|
||||
s->cputimer[i].timer = ptimer_init(slavio_timer_irq, tc,
|
||||
PTIMER_POLICY_DEFAULT);
|
||||
ptimer_transaction_begin(s->cputimer[i].timer);
|
||||
ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD);
|
||||
ptimer_transaction_commit(s->cputimer[i].timer);
|
||||
|
||||
size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE;
|
||||
snprintf(timer_name, sizeof(timer_name), "timer-%i", i);
|
||||
|
|
|
@ -87,3 +87,8 @@ pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
|||
pl031_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"
|
||||
pl031_alarm_raised(void) "alarm raised"
|
||||
pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks"
|
||||
|
||||
# bcm2835_systmr.c
|
||||
bcm2835_systmr_irq(bool enable) "timer irq state %u"
|
||||
bcm2835_systmr_read(uint64_t offset, uint64_t data) "timer read: offset 0x%" PRIx64 " data 0x%" PRIx64
|
||||
bcm2835_systmr_write(uint64_t offset, uint64_t data) "timer write: offset 0x%" PRIx64 " data 0x%" PRIx64
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include "hw/ptimer.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
#define D(x)
|
||||
|
@ -52,7 +51,6 @@
|
|||
|
||||
struct xlx_timer
|
||||
{
|
||||
QEMUBH *bh;
|
||||
ptimer_state *ptimer;
|
||||
void *parent;
|
||||
int nr; /* for debug. */
|
||||
|
@ -134,6 +132,7 @@ timer_read(void *opaque, hwaddr addr, unsigned int size)
|
|||
return r;
|
||||
}
|
||||
|
||||
/* Must be called inside ptimer transaction block */
|
||||
static void timer_enable(struct xlx_timer *xt)
|
||||
{
|
||||
uint64_t count;
|
||||
|
@ -174,8 +173,11 @@ timer_write(void *opaque, hwaddr addr,
|
|||
value &= ~TCSR_TINT;
|
||||
|
||||
xt->regs[addr] = value & 0x7ff;
|
||||
if (value & TCSR_ENT)
|
||||
if (value & TCSR_ENT) {
|
||||
ptimer_transaction_begin(xt->ptimer);
|
||||
timer_enable(xt);
|
||||
ptimer_transaction_commit(xt->ptimer);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -220,9 +222,10 @@ static void xilinx_timer_realize(DeviceState *dev, Error **errp)
|
|||
|
||||
xt->parent = t;
|
||||
xt->nr = i;
|
||||
xt->bh = qemu_bh_new(timer_hit, xt);
|
||||
xt->ptimer = ptimer_init_with_bh(xt->bh, PTIMER_POLICY_DEFAULT);
|
||||
xt->ptimer = ptimer_init(timer_hit, xt, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_transaction_begin(xt->ptimer);
|
||||
ptimer_set_freq(xt->ptimer, t->freq_hz);
|
||||
ptimer_transaction_commit(xt->ptimer);
|
||||
}
|
||||
|
||||
memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, "xlnx.xps-timer",
|
||||
|
|
|
@ -18,6 +18,7 @@ typedef struct AspeedBoardConfig {
|
|||
const char *desc;
|
||||
const char *soc_name;
|
||||
uint32_t hw_strap1;
|
||||
uint32_t hw_strap2;
|
||||
const char *fmc_model;
|
||||
const char *spi_model;
|
||||
uint32_t num_cs;
|
||||
|
|
|
@ -20,9 +20,11 @@
|
|||
#include "hw/misc/bcm2835_property.h"
|
||||
#include "hw/misc/bcm2835_rng.h"
|
||||
#include "hw/misc/bcm2835_mbox.h"
|
||||
#include "hw/misc/bcm2835_thermal.h"
|
||||
#include "hw/sd/sdhci.h"
|
||||
#include "hw/sd/bcm2835_sdhost.h"
|
||||
#include "hw/gpio/bcm2835_gpio.h"
|
||||
#include "hw/timer/bcm2835_systmr.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
|
||||
#define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals"
|
||||
|
@ -38,7 +40,7 @@ typedef struct BCM2835PeripheralState {
|
|||
MemoryRegion ram_alias[4];
|
||||
qemu_irq irq, fiq;
|
||||
|
||||
UnimplementedDeviceState systmr;
|
||||
BCM2835SystemTimerState systmr;
|
||||
UnimplementedDeviceState armtmr;
|
||||
UnimplementedDeviceState cprman;
|
||||
UnimplementedDeviceState a2w;
|
||||
|
@ -53,6 +55,7 @@ typedef struct BCM2835PeripheralState {
|
|||
SDHCIState sdhci;
|
||||
BCM2835SDHostState sdhost;
|
||||
BCM2835GpioState gpio;
|
||||
Bcm2835ThermalState thermal;
|
||||
UnimplementedDeviceState i2s;
|
||||
UnimplementedDeviceState spi[1];
|
||||
UnimplementedDeviceState i2c[3];
|
||||
|
|
|
@ -35,7 +35,9 @@ typedef struct BCM283XState {
|
|||
char *cpu_type;
|
||||
uint32_t enabled_cpus;
|
||||
|
||||
ARMCPU cpus[BCM283X_NCPUS];
|
||||
struct {
|
||||
ARMCPU core;
|
||||
} cpu[BCM283X_NCPUS];
|
||||
BCM2836ControlState control;
|
||||
BCM2835PeripheralState peripherals;
|
||||
} BCM283XState;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#define SPI0_OFFSET 0x204000
|
||||
#define BSC0_OFFSET 0x205000 /* BSC0 I2C/TWI */
|
||||
#define OTP_OFFSET 0x20f000
|
||||
#define THERMAL_OFFSET 0x212000
|
||||
#define BSC_SL_OFFSET 0x214000 /* SPI slave */
|
||||
#define AUX_OFFSET 0x215000 /* AUX: UART1/SPI1/SPI2 */
|
||||
#define EMMC1_OFFSET 0x300000
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* BCM2835 dummy thermal sensor
|
||||
*
|
||||
* Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HW_MISC_BCM2835_THERMAL_H
|
||||
#define HW_MISC_BCM2835_THERMAL_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_BCM2835_THERMAL "bcm2835-thermal"
|
||||
|
||||
#define BCM2835_THERMAL(obj) \
|
||||
OBJECT_CHECK(Bcm2835ThermalState, (obj), TYPE_BCM2835_THERMAL)
|
||||
|
||||
typedef struct {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
MemoryRegion iomem;
|
||||
uint32_t ctl;
|
||||
} Bcm2835ThermalState;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* BCM2835 SYS timer emulation
|
||||
*
|
||||
* Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BCM2835_SYSTIMER_H
|
||||
#define BCM2835_SYSTIMER_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_BCM2835_SYSTIMER "bcm2835-sys-timer"
|
||||
#define BCM2835_SYSTIMER(obj) \
|
||||
OBJECT_CHECK(BCM2835SystemTimerState, (obj), TYPE_BCM2835_SYSTIMER)
|
||||
|
||||
typedef struct {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
|
||||
struct {
|
||||
uint32_t status;
|
||||
uint32_t compare[4];
|
||||
} reg;
|
||||
} BCM2835SystemTimerState;
|
||||
|
||||
#endif
|
|
@ -173,6 +173,7 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
|||
for (i = 1; i < 4; ++i) {
|
||||
env->cp15.sctlr_el[i] |= SCTLR_EE;
|
||||
}
|
||||
arm_rebuild_hflags(env);
|
||||
#endif
|
||||
|
||||
if (cpu_isar_feature(aa64_pauth, cpu)) {
|
||||
|
|
|
@ -440,6 +440,7 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
|||
} else {
|
||||
env->cp15.sctlr_el[1] |= SCTLR_B;
|
||||
}
|
||||
arm_rebuild_hflags(env);
|
||||
#endif
|
||||
|
||||
ts->stack_base = info->start_stack;
|
||||
|
|
|
@ -9984,6 +9984,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
|
|||
aarch64_sve_narrow_vq(env, vq);
|
||||
}
|
||||
env->vfp.zcr_el[1] = vq - 1;
|
||||
arm_rebuild_hflags(env);
|
||||
ret = vq * 16;
|
||||
}
|
||||
return ret;
|
||||
|
|
|
@ -406,6 +406,7 @@ static void arm_cpu_reset(CPUState *s)
|
|||
|
||||
hw_breakpoint_update_all(cpu);
|
||||
hw_watchpoint_update_all(cpu);
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
|
||||
bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
|
|
|
@ -231,6 +231,9 @@ typedef struct CPUARMState {
|
|||
uint32_t pstate;
|
||||
uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */
|
||||
|
||||
/* Cached TBFLAGS state. See below for which bits are included. */
|
||||
uint32_t hflags;
|
||||
|
||||
/* Frequently accessed CPSR bits are stored separately for efficiency.
|
||||
This contains all the other bits. Use cpsr_{read,write} to access
|
||||
the whole CPSR. */
|
||||
|
@ -3105,33 +3108,44 @@ static inline uint64_t arm_sctlr(CPUARMState *env, int el)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool arm_cpu_data_is_big_endian_a32(CPUARMState *env,
|
||||
bool sctlr_b)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/*
|
||||
* In system mode, BE32 is modelled in line with the
|
||||
* architecture (as word-invariant big-endianness), where loads
|
||||
* and stores are done little endian but from addresses which
|
||||
* are adjusted by XORing with the appropriate constant. So the
|
||||
* endianness to use for the raw data access is not affected by
|
||||
* SCTLR.B.
|
||||
* In user mode, however, we model BE32 as byte-invariant
|
||||
* big-endianness (because user-only code cannot tell the
|
||||
* difference), and so we need to use a data access endianness
|
||||
* that depends on SCTLR.B.
|
||||
*/
|
||||
if (sctlr_b) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
/* In 32bit endianness is determined by looking at CPSR's E bit */
|
||||
return env->uncached_cpsr & CPSR_E;
|
||||
}
|
||||
|
||||
static inline bool arm_cpu_data_is_big_endian_a64(int el, uint64_t sctlr)
|
||||
{
|
||||
return sctlr & (el ? SCTLR_EE : SCTLR_E0E);
|
||||
}
|
||||
|
||||
/* Return true if the processor is in big-endian mode. */
|
||||
static inline bool arm_cpu_data_is_big_endian(CPUARMState *env)
|
||||
{
|
||||
/* In 32bit endianness is determined by looking at CPSR's E bit */
|
||||
if (!is_a64(env)) {
|
||||
return
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* In system mode, BE32 is modelled in line with the
|
||||
* architecture (as word-invariant big-endianness), where loads
|
||||
* and stores are done little endian but from addresses which
|
||||
* are adjusted by XORing with the appropriate constant. So the
|
||||
* endianness to use for the raw data access is not affected by
|
||||
* SCTLR.B.
|
||||
* In user mode, however, we model BE32 as byte-invariant
|
||||
* big-endianness (because user-only code cannot tell the
|
||||
* difference), and so we need to use a data access endianness
|
||||
* that depends on SCTLR.B.
|
||||
*/
|
||||
arm_sctlr_b(env) ||
|
||||
#endif
|
||||
((env->uncached_cpsr & CPSR_E) ? 1 : 0);
|
||||
return arm_cpu_data_is_big_endian_a32(env, arm_sctlr_b(env));
|
||||
} else {
|
||||
int cur_el = arm_current_el(env);
|
||||
uint64_t sctlr = arm_sctlr(env, cur_el);
|
||||
|
||||
return (sctlr & (cur_el ? SCTLR_EE : SCTLR_E0E)) != 0;
|
||||
return arm_cpu_data_is_big_endian_a64(cur_el, sctlr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3140,15 +3154,18 @@ typedef ARMCPU ArchCPU;
|
|||
|
||||
#include "exec/cpu-all.h"
|
||||
|
||||
/* Bit usage in the TB flags field: bit 31 indicates whether we are
|
||||
/*
|
||||
* Bit usage in the TB flags field: bit 31 indicates whether we are
|
||||
* in 32 or 64 bit mode. The meaning of the other bits depends on that.
|
||||
* We put flags which are shared between 32 and 64 bit mode at the top
|
||||
* of the word, and flags which apply to only one mode at the bottom.
|
||||
*
|
||||
* Unless otherwise noted, these bits are cached in env->hflags.
|
||||
*/
|
||||
FIELD(TBFLAG_ANY, AARCH64_STATE, 31, 1)
|
||||
FIELD(TBFLAG_ANY, MMUIDX, 28, 3)
|
||||
FIELD(TBFLAG_ANY, SS_ACTIVE, 27, 1)
|
||||
FIELD(TBFLAG_ANY, PSTATE_SS, 26, 1)
|
||||
FIELD(TBFLAG_ANY, PSTATE_SS, 26, 1) /* Not cached. */
|
||||
/* Target EL if we take a floating-point-disabled exception */
|
||||
FIELD(TBFLAG_ANY, FPEXC_EL, 24, 2)
|
||||
FIELD(TBFLAG_ANY, BE_DATA, 23, 1)
|
||||
|
@ -3159,13 +3176,14 @@ FIELD(TBFLAG_ANY, BE_DATA, 23, 1)
|
|||
FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 21, 2)
|
||||
|
||||
/* Bit usage when in AArch32 state: */
|
||||
FIELD(TBFLAG_A32, THUMB, 0, 1)
|
||||
FIELD(TBFLAG_A32, VECLEN, 1, 3)
|
||||
FIELD(TBFLAG_A32, VECSTRIDE, 4, 2)
|
||||
FIELD(TBFLAG_A32, THUMB, 0, 1) /* Not cached. */
|
||||
FIELD(TBFLAG_A32, VECLEN, 1, 3) /* Not cached. */
|
||||
FIELD(TBFLAG_A32, VECSTRIDE, 4, 2) /* Not cached. */
|
||||
/*
|
||||
* We store the bottom two bits of the CPAR as TB flags and handle
|
||||
* checks on the other bits at runtime. This shares the same bits as
|
||||
* VECSTRIDE, which is OK as no XScale CPU has VFP.
|
||||
* Not cached, because VECLEN+VECSTRIDE are not cached.
|
||||
*/
|
||||
FIELD(TBFLAG_A32, XSCALE_CPAR, 4, 2)
|
||||
/*
|
||||
|
@ -3174,15 +3192,15 @@ FIELD(TBFLAG_A32, XSCALE_CPAR, 4, 2)
|
|||
* the same thing as the current security state of the processor!
|
||||
*/
|
||||
FIELD(TBFLAG_A32, NS, 6, 1)
|
||||
FIELD(TBFLAG_A32, VFPEN, 7, 1)
|
||||
FIELD(TBFLAG_A32, CONDEXEC, 8, 8)
|
||||
FIELD(TBFLAG_A32, VFPEN, 7, 1) /* Partially cached, minus FPEXC. */
|
||||
FIELD(TBFLAG_A32, CONDEXEC, 8, 8) /* Not cached. */
|
||||
FIELD(TBFLAG_A32, SCTLR_B, 16, 1)
|
||||
/* For M profile only, set if FPCCR.LSPACT is set */
|
||||
FIELD(TBFLAG_A32, LSPACT, 18, 1)
|
||||
FIELD(TBFLAG_A32, LSPACT, 18, 1) /* Not cached. */
|
||||
/* For M profile only, set if we must create a new FP context */
|
||||
FIELD(TBFLAG_A32, NEW_FP_CTXT_NEEDED, 19, 1)
|
||||
FIELD(TBFLAG_A32, NEW_FP_CTXT_NEEDED, 19, 1) /* Not cached. */
|
||||
/* For M profile only, set if FPCCR.S does not match current security state */
|
||||
FIELD(TBFLAG_A32, FPCCR_S_WRONG, 20, 1)
|
||||
FIELD(TBFLAG_A32, FPCCR_S_WRONG, 20, 1) /* Not cached. */
|
||||
/* For M profile only, Handler (ie not Thread) mode */
|
||||
FIELD(TBFLAG_A32, HANDLER, 21, 1)
|
||||
/* For M profile only, whether we should generate stack-limit checks */
|
||||
|
@ -3194,7 +3212,7 @@ FIELD(TBFLAG_A64, SVEEXC_EL, 2, 2)
|
|||
FIELD(TBFLAG_A64, ZCR_LEN, 4, 4)
|
||||
FIELD(TBFLAG_A64, PAUTH_ACTIVE, 8, 1)
|
||||
FIELD(TBFLAG_A64, BT, 9, 1)
|
||||
FIELD(TBFLAG_A64, BTYPE, 10, 2)
|
||||
FIELD(TBFLAG_A64, BTYPE, 10, 2) /* Not cached. */
|
||||
FIELD(TBFLAG_A64, TBID, 12, 2)
|
||||
|
||||
static inline bool bswap_code(bool sctlr_b)
|
||||
|
@ -3279,6 +3297,12 @@ void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
|
|||
void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void
|
||||
*opaque);
|
||||
|
||||
/**
|
||||
* arm_rebuild_hflags:
|
||||
* Rebuild the cached TBFLAGS for arbitrary changed processor state.
|
||||
*/
|
||||
void arm_rebuild_hflags(CPUARMState *env);
|
||||
|
||||
/**
|
||||
* aa32_vfp_dreg:
|
||||
* Return a pointer to the Dn register within env in 32-bit mode.
|
||||
|
|
|
@ -1025,6 +1025,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc)
|
|||
} else {
|
||||
env->regs[15] = new_pc & ~0x3;
|
||||
}
|
||||
helper_rebuild_hflags_a32(env, new_el);
|
||||
qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to "
|
||||
"AArch32 EL%d PC 0x%" PRIx32 "\n",
|
||||
cur_el, new_el, env->regs[15]);
|
||||
|
@ -1036,10 +1037,12 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc)
|
|||
}
|
||||
aarch64_restore_sp(env, new_el);
|
||||
env->pc = new_pc;
|
||||
helper_rebuild_hflags_a64(env, new_el);
|
||||
qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to "
|
||||
"AArch64 EL%d PC 0x%" PRIx64 "\n",
|
||||
cur_el, new_el, env->pc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that cur_el can never be 0. If new_el is 0, then
|
||||
* el0_a64 is return_to_aa64, else el0_a64 is ignored.
|
||||
|
|
|
@ -4174,6 +4174,16 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|||
/* ??? Lots of these bits are not implemented. */
|
||||
/* This may enable/disable the MMU, so do a TLB flush. */
|
||||
tlb_flush(CPU(cpu));
|
||||
|
||||
if (ri->type & ARM_CP_SUPPRESS_TB_END) {
|
||||
/*
|
||||
* Normally we would always end the TB on an SCTLR write; see the
|
||||
* comment in ARMCPRegInfo sctlr initialization below for why Xscale
|
||||
* is special. Setting ARM_CP_SUPPRESS_TB_END also stops the rebuild
|
||||
* of hflags from the translator, so do it here.
|
||||
*/
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
}
|
||||
|
||||
static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
|
@ -7998,6 +8008,7 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode,
|
|||
env->regs[14] = env->regs[15] + offset;
|
||||
}
|
||||
env->regs[15] = newpc;
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
|
||||
static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs)
|
||||
|
@ -8345,6 +8356,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
|
|||
pstate_write(env, PSTATE_DAIF | new_mode);
|
||||
env->aarch64 = 1;
|
||||
aarch64_restore_sp(env, new_el);
|
||||
helper_rebuild_hflags_a64(env, new_el);
|
||||
|
||||
env->pc = addr;
|
||||
|
||||
|
@ -11026,15 +11038,12 @@ ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate)
|
|||
}
|
||||
#endif
|
||||
|
||||
ARMMMUIdx arm_mmu_idx(CPUARMState *env)
|
||||
ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el)
|
||||
{
|
||||
int el;
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
return arm_v7m_mmu_idx_for_secstate(env, env->v7m.secure);
|
||||
}
|
||||
|
||||
el = arm_current_el(env);
|
||||
if (el < 2 && arm_is_secure_below_el3(env)) {
|
||||
return ARMMMUIdx_S1SE0 + el;
|
||||
} else {
|
||||
|
@ -11042,6 +11051,11 @@ ARMMMUIdx arm_mmu_idx(CPUARMState *env)
|
|||
}
|
||||
}
|
||||
|
||||
ARMMMUIdx arm_mmu_idx(CPUARMState *env)
|
||||
{
|
||||
return arm_mmu_idx_el(env, arm_current_el(env));
|
||||
}
|
||||
|
||||
int cpu_mmu_index(CPUARMState *env, bool ifetch)
|
||||
{
|
||||
return arm_to_core_mmu_idx(arm_mmu_idx(env));
|
||||
|
@ -11054,171 +11068,276 @@ ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env)
|
|||
}
|
||||
#endif
|
||||
|
||||
void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
|
||||
target_ulong *cs_base, uint32_t *pflags)
|
||||
static uint32_t rebuild_hflags_common(CPUARMState *env, int fp_el,
|
||||
ARMMMUIdx mmu_idx, uint32_t flags)
|
||||
{
|
||||
ARMMMUIdx mmu_idx = arm_mmu_idx(env);
|
||||
int current_el = arm_current_el(env);
|
||||
int fp_el = fp_exception_el(env, current_el);
|
||||
uint32_t flags = 0;
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, FPEXC_EL, fp_el);
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, MMUIDX,
|
||||
arm_to_core_mmu_idx(mmu_idx));
|
||||
|
||||
if (is_a64(env)) {
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
uint64_t sctlr;
|
||||
|
||||
*pc = env->pc;
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, AARCH64_STATE, 1);
|
||||
|
||||
/* Get control bits for tagged addresses. */
|
||||
{
|
||||
ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx);
|
||||
ARMVAParameters p0 = aa64_va_parameters_both(env, 0, stage1);
|
||||
int tbii, tbid;
|
||||
|
||||
/* FIXME: ARMv8.1-VHE S2 translation regime. */
|
||||
if (regime_el(env, stage1) < 2) {
|
||||
ARMVAParameters p1 = aa64_va_parameters_both(env, -1, stage1);
|
||||
tbid = (p1.tbi << 1) | p0.tbi;
|
||||
tbii = tbid & ~((p1.tbid << 1) | p0.tbid);
|
||||
} else {
|
||||
tbid = p0.tbi;
|
||||
tbii = tbid & !p0.tbid;
|
||||
}
|
||||
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, TBII, tbii);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, TBID, tbid);
|
||||
}
|
||||
|
||||
if (cpu_isar_feature(aa64_sve, cpu)) {
|
||||
int sve_el = sve_exception_el(env, current_el);
|
||||
uint32_t zcr_len;
|
||||
|
||||
/* If SVE is disabled, but FP is enabled,
|
||||
* then the effective len is 0.
|
||||
*/
|
||||
if (sve_el != 0 && fp_el == 0) {
|
||||
zcr_len = 0;
|
||||
} else {
|
||||
zcr_len = sve_zcr_len_for_el(env, current_el);
|
||||
}
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, SVEEXC_EL, sve_el);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, ZCR_LEN, zcr_len);
|
||||
}
|
||||
|
||||
sctlr = arm_sctlr(env, current_el);
|
||||
|
||||
if (cpu_isar_feature(aa64_pauth, cpu)) {
|
||||
/*
|
||||
* In order to save space in flags, we record only whether
|
||||
* pauth is "inactive", meaning all insns are implemented as
|
||||
* a nop, or "active" when some action must be performed.
|
||||
* The decision of which action to take is left to a helper.
|
||||
*/
|
||||
if (sctlr & (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, PAUTH_ACTIVE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_isar_feature(aa64_bti, cpu)) {
|
||||
/* Note that SCTLR_EL[23].BT == SCTLR_BT1. */
|
||||
if (sctlr & (current_el == 0 ? SCTLR_BT0 : SCTLR_BT1)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, BT, 1);
|
||||
}
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, BTYPE, env->btype);
|
||||
}
|
||||
} else {
|
||||
*pc = env->regs[15];
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, THUMB, env->thumb);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, VECLEN, env->vfp.vec_len);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, VECSTRIDE, env->vfp.vec_stride);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, CONDEXEC, env->condexec_bits);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, SCTLR_B, arm_sctlr_b(env));
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, NS, !access_secure_reg(env));
|
||||
if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)
|
||||
|| arm_el_is_aa64(env, 1) || arm_feature(env, ARM_FEATURE_M)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1);
|
||||
}
|
||||
/* Note that XSCALE_CPAR shares bits with VECSTRIDE */
|
||||
if (arm_feature(env, ARM_FEATURE_XSCALE)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32,
|
||||
XSCALE_CPAR, env->cp15.c15_cpar);
|
||||
}
|
||||
}
|
||||
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, MMUIDX, arm_to_core_mmu_idx(mmu_idx));
|
||||
|
||||
/* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine
|
||||
* states defined in the ARM ARM for software singlestep:
|
||||
* SS_ACTIVE PSTATE.SS State
|
||||
* 0 x Inactive (the TB flag for SS is always 0)
|
||||
* 1 0 Active-pending
|
||||
* 1 1 Active-not-pending
|
||||
*/
|
||||
if (arm_singlestep_active(env)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, SS_ACTIVE, 1);
|
||||
if (is_a64(env)) {
|
||||
if (env->pstate & PSTATE_SS) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, PSTATE_SS, 1);
|
||||
}
|
||||
} else {
|
||||
if (env->uncached_cpsr & PSTATE_SS) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, PSTATE_SS, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (arm_cpu_data_is_big_endian(env)) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
static uint32_t rebuild_hflags_common_32(CPUARMState *env, int fp_el,
|
||||
ARMMMUIdx mmu_idx, uint32_t flags)
|
||||
{
|
||||
bool sctlr_b = arm_sctlr_b(env);
|
||||
|
||||
if (sctlr_b) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, SCTLR_B, 1);
|
||||
}
|
||||
if (arm_cpu_data_is_big_endian_a32(env, sctlr_b)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, BE_DATA, 1);
|
||||
}
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, FPEXC_EL, fp_el);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, NS, !access_secure_reg(env));
|
||||
|
||||
return rebuild_hflags_common(env, fp_el, mmu_idx, flags);
|
||||
}
|
||||
|
||||
static uint32_t rebuild_hflags_m32(CPUARMState *env, int fp_el,
|
||||
ARMMMUIdx mmu_idx)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
|
||||
/* v8M always enables the fpu. */
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1);
|
||||
|
||||
if (arm_v7m_is_handler_mode(env)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, HANDLER, 1);
|
||||
}
|
||||
|
||||
/* v8M always applies stack limit checks unless CCR.STKOFHFNMIGN is
|
||||
* suppressing them because the requested execution priority is less than 0.
|
||||
/*
|
||||
* v8M always applies stack limit checks unless CCR.STKOFHFNMIGN
|
||||
* is suppressing them because the requested execution priority
|
||||
* is less than 0.
|
||||
*/
|
||||
if (arm_feature(env, ARM_FEATURE_V8) &&
|
||||
arm_feature(env, ARM_FEATURE_M) &&
|
||||
!((mmu_idx & ARM_MMU_IDX_M_NEGPRI) &&
|
||||
!((mmu_idx & ARM_MMU_IDX_M_NEGPRI) &&
|
||||
(env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKOFHFNMIGN_MASK))) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, STACKCHECK, 1);
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_M_SECURITY) &&
|
||||
FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S) != env->v7m.secure) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, FPCCR_S_WRONG, 1);
|
||||
return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags);
|
||||
}
|
||||
|
||||
static uint32_t rebuild_hflags_aprofile(CPUARMState *env)
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, DEBUG_TARGET_EL,
|
||||
arm_debug_target_el(env));
|
||||
return flags;
|
||||
}
|
||||
|
||||
static uint32_t rebuild_hflags_a32(CPUARMState *env, int fp_el,
|
||||
ARMMMUIdx mmu_idx)
|
||||
{
|
||||
uint32_t flags = rebuild_hflags_aprofile(env);
|
||||
|
||||
if (arm_el_is_aa64(env, 1)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1);
|
||||
}
|
||||
return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags);
|
||||
}
|
||||
|
||||
static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
|
||||
ARMMMUIdx mmu_idx)
|
||||
{
|
||||
uint32_t flags = rebuild_hflags_aprofile(env);
|
||||
ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx);
|
||||
ARMVAParameters p0 = aa64_va_parameters_both(env, 0, stage1);
|
||||
uint64_t sctlr;
|
||||
int tbii, tbid;
|
||||
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, AARCH64_STATE, 1);
|
||||
|
||||
/* FIXME: ARMv8.1-VHE S2 translation regime. */
|
||||
if (regime_el(env, stage1) < 2) {
|
||||
ARMVAParameters p1 = aa64_va_parameters_both(env, -1, stage1);
|
||||
tbid = (p1.tbi << 1) | p0.tbi;
|
||||
tbii = tbid & ~((p1.tbid << 1) | p0.tbid);
|
||||
} else {
|
||||
tbid = p0.tbi;
|
||||
tbii = tbid & !p0.tbid;
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_M) &&
|
||||
(env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) &&
|
||||
(!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) ||
|
||||
(env->v7m.secure &&
|
||||
!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)))) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, TBII, tbii);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, TBID, tbid);
|
||||
|
||||
if (cpu_isar_feature(aa64_sve, env_archcpu(env))) {
|
||||
int sve_el = sve_exception_el(env, el);
|
||||
uint32_t zcr_len;
|
||||
|
||||
/*
|
||||
* ASPEN is set, but FPCA/SFPA indicate that there is no active
|
||||
* FP context; we must create a new FP context before executing
|
||||
* any FP insn.
|
||||
* If SVE is disabled, but FP is enabled,
|
||||
* then the effective len is 0.
|
||||
*/
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, NEW_FP_CTXT_NEEDED, 1);
|
||||
if (sve_el != 0 && fp_el == 0) {
|
||||
zcr_len = 0;
|
||||
} else {
|
||||
zcr_len = sve_zcr_len_for_el(env, el);
|
||||
}
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, SVEEXC_EL, sve_el);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, ZCR_LEN, zcr_len);
|
||||
}
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK;
|
||||
sctlr = arm_sctlr(env, el);
|
||||
|
||||
if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, LSPACT, 1);
|
||||
if (arm_cpu_data_is_big_endian_a64(el, sctlr)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, BE_DATA, 1);
|
||||
}
|
||||
|
||||
if (cpu_isar_feature(aa64_pauth, env_archcpu(env))) {
|
||||
/*
|
||||
* In order to save space in flags, we record only whether
|
||||
* pauth is "inactive", meaning all insns are implemented as
|
||||
* a nop, or "active" when some action must be performed.
|
||||
* The decision of which action to take is left to a helper.
|
||||
*/
|
||||
if (sctlr & (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, PAUTH_ACTIVE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!arm_feature(env, ARM_FEATURE_M)) {
|
||||
int target_el = arm_debug_target_el(env);
|
||||
if (cpu_isar_feature(aa64_bti, env_archcpu(env))) {
|
||||
/* Note that SCTLR_EL[23].BT == SCTLR_BT1. */
|
||||
if (sctlr & (el == 0 ? SCTLR_BT0 : SCTLR_BT1)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, BT, 1);
|
||||
}
|
||||
}
|
||||
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, DEBUG_TARGET_EL, target_el);
|
||||
return rebuild_hflags_common(env, fp_el, mmu_idx, flags);
|
||||
}
|
||||
|
||||
static uint32_t rebuild_hflags_internal(CPUARMState *env)
|
||||
{
|
||||
int el = arm_current_el(env);
|
||||
int fp_el = fp_exception_el(env, el);
|
||||
ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el);
|
||||
|
||||
if (is_a64(env)) {
|
||||
return rebuild_hflags_a64(env, el, fp_el, mmu_idx);
|
||||
} else if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
return rebuild_hflags_m32(env, fp_el, mmu_idx);
|
||||
} else {
|
||||
return rebuild_hflags_a32(env, fp_el, mmu_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void arm_rebuild_hflags(CPUARMState *env)
|
||||
{
|
||||
env->hflags = rebuild_hflags_internal(env);
|
||||
}
|
||||
|
||||
void HELPER(rebuild_hflags_m32)(CPUARMState *env, int el)
|
||||
{
|
||||
int fp_el = fp_exception_el(env, el);
|
||||
ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el);
|
||||
|
||||
env->hflags = rebuild_hflags_m32(env, fp_el, mmu_idx);
|
||||
}
|
||||
|
||||
void HELPER(rebuild_hflags_a32)(CPUARMState *env, int el)
|
||||
{
|
||||
int fp_el = fp_exception_el(env, el);
|
||||
ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el);
|
||||
|
||||
env->hflags = rebuild_hflags_a32(env, fp_el, mmu_idx);
|
||||
}
|
||||
|
||||
void HELPER(rebuild_hflags_a64)(CPUARMState *env, int el)
|
||||
{
|
||||
int fp_el = fp_exception_el(env, el);
|
||||
ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el);
|
||||
|
||||
env->hflags = rebuild_hflags_a64(env, el, fp_el, mmu_idx);
|
||||
}
|
||||
|
||||
void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
|
||||
target_ulong *cs_base, uint32_t *pflags)
|
||||
{
|
||||
uint32_t flags = env->hflags;
|
||||
uint32_t pstate_for_ss;
|
||||
|
||||
*cs_base = 0;
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
assert(flags == rebuild_hflags_internal(env));
|
||||
#endif
|
||||
|
||||
if (FIELD_EX32(flags, TBFLAG_ANY, AARCH64_STATE)) {
|
||||
*pc = env->pc;
|
||||
if (cpu_isar_feature(aa64_bti, env_archcpu(env))) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A64, BTYPE, env->btype);
|
||||
}
|
||||
pstate_for_ss = env->pstate;
|
||||
} else {
|
||||
*pc = env->regs[15];
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_M)) {
|
||||
if (arm_feature(env, ARM_FEATURE_M_SECURITY) &&
|
||||
FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S)
|
||||
!= env->v7m.secure) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, FPCCR_S_WRONG, 1);
|
||||
}
|
||||
|
||||
if ((env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) &&
|
||||
(!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) ||
|
||||
(env->v7m.secure &&
|
||||
!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)))) {
|
||||
/*
|
||||
* ASPEN is set, but FPCA/SFPA indicate that there is no
|
||||
* active FP context; we must create a new FP context before
|
||||
* executing any FP insn.
|
||||
*/
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, NEW_FP_CTXT_NEEDED, 1);
|
||||
}
|
||||
|
||||
bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK;
|
||||
if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, LSPACT, 1);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Note that XSCALE_CPAR shares bits with VECSTRIDE.
|
||||
* Note that VECLEN+VECSTRIDE are RES0 for M-profile.
|
||||
*/
|
||||
if (arm_feature(env, ARM_FEATURE_XSCALE)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32,
|
||||
XSCALE_CPAR, env->cp15.c15_cpar);
|
||||
} else {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, VECLEN,
|
||||
env->vfp.vec_len);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, VECSTRIDE,
|
||||
env->vfp.vec_stride);
|
||||
}
|
||||
if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1);
|
||||
}
|
||||
}
|
||||
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, THUMB, env->thumb);
|
||||
flags = FIELD_DP32(flags, TBFLAG_A32, CONDEXEC, env->condexec_bits);
|
||||
pstate_for_ss = env->uncached_cpsr;
|
||||
}
|
||||
|
||||
/*
|
||||
* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine
|
||||
* states defined in the ARM ARM for software singlestep:
|
||||
* SS_ACTIVE PSTATE.SS State
|
||||
* 0 x Inactive (the TB flag for SS is always 0)
|
||||
* 1 0 Active-pending
|
||||
* 1 1 Active-not-pending
|
||||
* SS_ACTIVE is set in hflags; PSTATE_SS is computed every TB.
|
||||
*/
|
||||
if (FIELD_EX32(flags, TBFLAG_ANY, SS_ACTIVE) &&
|
||||
(pstate_for_ss & PSTATE_SS)) {
|
||||
flags = FIELD_DP32(flags, TBFLAG_ANY, PSTATE_SS, 1);
|
||||
}
|
||||
|
||||
*pflags = flags;
|
||||
*cs_base = 0;
|
||||
}
|
||||
|
||||
#ifdef TARGET_AARCH64
|
||||
|
|
|
@ -90,6 +90,10 @@ DEF_HELPER_4(msr_banked, void, env, i32, i32, i32)
|
|||
DEF_HELPER_2(get_user_reg, i32, env, i32)
|
||||
DEF_HELPER_3(set_user_reg, void, env, i32, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_2(rebuild_hflags_m32, TCG_CALL_NO_RWG, void, env, int)
|
||||
DEF_HELPER_FLAGS_2(rebuild_hflags_a32, TCG_CALL_NO_RWG, void, env, int)
|
||||
DEF_HELPER_FLAGS_2(rebuild_hflags_a64, TCG_CALL_NO_RWG, void, env, int)
|
||||
|
||||
DEF_HELPER_1(vfp_get_fpscr, i32, env)
|
||||
DEF_HELPER_2(vfp_set_fpscr, void, env, i32)
|
||||
|
||||
|
|
|
@ -949,6 +949,15 @@ void arm_cpu_update_virq(ARMCPU *cpu);
|
|||
*/
|
||||
void arm_cpu_update_vfiq(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* arm_mmu_idx_el:
|
||||
* @env: The cpu environment
|
||||
* @el: The EL to use.
|
||||
*
|
||||
* Return the full ARMMMUIdx for the translation regime for EL.
|
||||
*/
|
||||
ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el);
|
||||
|
||||
/**
|
||||
* arm_mmu_idx:
|
||||
* @env: The cpu environment
|
||||
|
|
|
@ -494,6 +494,7 @@ void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest)
|
|||
switch_v7m_security_state(env, dest & 1);
|
||||
env->thumb = 1;
|
||||
env->regs[15] = dest & ~1;
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
|
||||
void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest)
|
||||
|
@ -555,6 +556,7 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest)
|
|||
switch_v7m_security_state(env, 0);
|
||||
env->thumb = 1;
|
||||
env->regs[15] = dest;
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
|
||||
static uint32_t *get_v7m_sp_ptr(CPUARMState *env, bool secure, bool threadmode,
|
||||
|
@ -895,6 +897,7 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain,
|
|||
env->regs[14] = lr;
|
||||
env->regs[15] = addr & 0xfffffffe;
|
||||
env->thumb = addr & 1;
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
|
||||
static void v7m_update_fpccr(CPUARMState *env, uint32_t frameptr,
|
||||
|
@ -1765,6 +1768,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
|
|||
|
||||
/* Otherwise, we have a successful exception exit. */
|
||||
arm_clear_exclusive(env);
|
||||
arm_rebuild_hflags(env);
|
||||
qemu_log_mask(CPU_LOG_INT, "...successful exception return\n");
|
||||
}
|
||||
|
||||
|
@ -1837,6 +1841,7 @@ static bool do_v7m_function_return(ARMCPU *cpu)
|
|||
xpsr_write(env, 0, XPSR_IT);
|
||||
env->thumb = newpc & 1;
|
||||
env->regs[15] = newpc & ~1;
|
||||
arm_rebuild_hflags(env);
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "...function return successful\n");
|
||||
return true;
|
||||
|
@ -1959,6 +1964,7 @@ static bool v7m_handle_execute_nsc(ARMCPU *cpu)
|
|||
switch_v7m_security_state(env, true);
|
||||
xpsr_write(env, 0, XPSR_IT);
|
||||
env->regs[15] += 4;
|
||||
arm_rebuild_hflags(env);
|
||||
return true;
|
||||
|
||||
gen_invep:
|
||||
|
|
|
@ -756,6 +756,7 @@ static int cpu_post_load(void *opaque, int version_id)
|
|||
if (!kvm_enabled()) {
|
||||
pmu_op_finish(&cpu->env);
|
||||
}
|
||||
arm_rebuild_hflags(&cpu->env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@ uint32_t HELPER(usat16)(CPUARMState *env, uint32_t x, uint32_t shift)
|
|||
void HELPER(setend)(CPUARMState *env)
|
||||
{
|
||||
env->uncached_cpsr ^= CPSR_E;
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
|
||||
/* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped.
|
||||
|
@ -387,6 +388,8 @@ uint32_t HELPER(cpsr_read)(CPUARMState *env)
|
|||
void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask)
|
||||
{
|
||||
cpsr_write(env, val, mask, CPSRWriteByInstr);
|
||||
/* TODO: Not all cpsr bits are relevant to hflags. */
|
||||
arm_rebuild_hflags(env);
|
||||
}
|
||||
|
||||
/* Write the CPSR for a 32-bit exception return */
|
||||
|
@ -404,6 +407,7 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
|
|||
* state. Do the masking now.
|
||||
*/
|
||||
env->regs[15] &= (env->thumb ? ~1 : ~3);
|
||||
arm_rebuild_hflags(env);
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
arm_call_el_change_hook(env_archcpu(env));
|
||||
|
|
|
@ -1789,8 +1789,17 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
|
|||
if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {
|
||||
/* I/O operations must end the TB here (whether read or write) */
|
||||
s->base.is_jmp = DISAS_UPDATE;
|
||||
} else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
|
||||
/* We default to ending the TB on a coprocessor register write,
|
||||
}
|
||||
if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
|
||||
/*
|
||||
* A write to any coprocessor regiser that ends a TB
|
||||
* must rebuild the hflags for the next TB.
|
||||
*/
|
||||
TCGv_i32 tcg_el = tcg_const_i32(s->current_el);
|
||||
gen_helper_rebuild_hflags_a64(cpu_env, tcg_el);
|
||||
tcg_temp_free_i32(tcg_el);
|
||||
/*
|
||||
* We default to ending the TB on a coprocessor register write,
|
||||
* but allow this to be suppressed by the register definition
|
||||
* (usually only necessary to work around guest bugs).
|
||||
*/
|
||||
|
|
|
@ -6890,6 +6890,8 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn)
|
|||
ri = get_arm_cp_reginfo(s->cp_regs,
|
||||
ENCODE_CP_REG(cpnum, is64, s->ns, crn, crm, opc1, opc2));
|
||||
if (ri) {
|
||||
bool need_exit_tb;
|
||||
|
||||
/* Check access permissions */
|
||||
if (!cp_access_ok(s->current_el, ri, isread)) {
|
||||
return 1;
|
||||
|
@ -7068,14 +7070,30 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn)
|
|||
}
|
||||
}
|
||||
|
||||
if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {
|
||||
/* I/O operations must end the TB here (whether read or write) */
|
||||
gen_lookup_tb(s);
|
||||
} else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
|
||||
/* We default to ending the TB on a coprocessor register write,
|
||||
/* I/O operations must end the TB here (whether read or write) */
|
||||
need_exit_tb = ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) &&
|
||||
(ri->type & ARM_CP_IO));
|
||||
|
||||
if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
|
||||
/*
|
||||
* A write to any coprocessor regiser that ends a TB
|
||||
* must rebuild the hflags for the next TB.
|
||||
*/
|
||||
TCGv_i32 tcg_el = tcg_const_i32(s->current_el);
|
||||
if (arm_dc_feature(s, ARM_FEATURE_M)) {
|
||||
gen_helper_rebuild_hflags_m32(cpu_env, tcg_el);
|
||||
} else {
|
||||
gen_helper_rebuild_hflags_a32(cpu_env, tcg_el);
|
||||
}
|
||||
tcg_temp_free_i32(tcg_el);
|
||||
/*
|
||||
* We default to ending the TB on a coprocessor register write,
|
||||
* but allow this to be suppressed by the register definition
|
||||
* (usually only necessary to work around guest bugs).
|
||||
*/
|
||||
need_exit_tb = true;
|
||||
}
|
||||
if (need_exit_tb) {
|
||||
gen_lookup_tb(s);
|
||||
}
|
||||
|
||||
|
@ -8309,7 +8327,7 @@ static bool trans_MRS_v7m(DisasContext *s, arg_MRS_v7m *a)
|
|||
|
||||
static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a)
|
||||
{
|
||||
TCGv_i32 addr, reg;
|
||||
TCGv_i32 addr, reg, el;
|
||||
|
||||
if (!arm_dc_feature(s, ARM_FEATURE_M)) {
|
||||
return false;
|
||||
|
@ -8319,6 +8337,9 @@ static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a)
|
|||
gen_helper_v7m_msr(cpu_env, addr, reg);
|
||||
tcg_temp_free_i32(addr);
|
||||
tcg_temp_free_i32(reg);
|
||||
el = tcg_const_i32(s->current_el);
|
||||
gen_helper_rebuild_hflags_m32(cpu_env, el);
|
||||
tcg_temp_free_i32(el);
|
||||
gen_lookup_tb(s);
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue