diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 144dc491d9..520fd39071 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -24,9 +24,12 @@ the following architecture extensions: - FEAT_I8MM (AArch64 Int8 matrix multiplication instructions) - FEAT_JSCVT (JavaScript conversion instructions) - FEAT_LOR (Limited ordering regions) +- FEAT_LPA (Large Physical Address space) +- FEAT_LPA2 (Large Physical and virtual Address space v2) - FEAT_LRCPC (Load-acquire RCpc instructions) - FEAT_LRCPC2 (Load-acquire RCpc instructions v2) - FEAT_LSE (Large System Extensions) +- FEAT_LVA (Large Virtual Address space) - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index b1e95978f2..a47f38dfc9 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -488,9 +488,14 @@ static void fdt_add_psci_node(void *fdt) } qemu_fdt_add_subnode(fdt, "/psci"); - if (armcpu->psci_version == 2) { - const char comp[] = "arm,psci-0.2\0arm,psci"; - qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp)); + if (armcpu->psci_version >= QEMU_PSCI_VERSION_0_2) { + if (armcpu->psci_version < QEMU_PSCI_VERSION_1_0) { + const char comp[] = "arm,psci-0.2\0arm,psci"; + qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp)); + } else { + const char comp[] = "arm,psci-1.0\0arm,psci-0.2\0arm,psci"; + qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp)); + } cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF; if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) { diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index f40e854dec..4017392bf5 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -32,7 +32,7 @@ * Application Note AN524: * https://developer.arm.com/documentation/dai0524/latest/ * Application Note AN547: - * https://developer.arm.com/-/media/Arm%20Developer%20Community/PDF/DAI0547B_SSE300_PLUS_U55_FPGA_for_mps3.pdf + * https://developer.arm.com/documentation/dai0547/latest/ * * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide * (ARM ECM0601256) for the details of some of the device layout: @@ -1078,6 +1078,10 @@ static void mps2tz_common_init(MachineState *machine) { "gpio1", make_unimp_dev, &mms->gpio[1], 0x41101000, 0x1000 }, { "gpio2", make_unimp_dev, &mms->gpio[2], 0x41102000, 0x1000 }, { "gpio3", make_unimp_dev, &mms->gpio[3], 0x41103000, 0x1000 }, + { /* port 4 USER AHB interface 0 */ }, + { /* port 5 USER AHB interface 1 */ }, + { /* port 6 USER AHB interface 2 */ }, + { /* port 7 USER AHB interface 3 */ }, { "eth-usb", make_eth_usb, NULL, 0x41400000, 0x200000, { 49 } }, }, }, diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c index 847c59921c..a9517b612a 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -30,24 +30,6 @@ #define PCA9548_CHANNEL_COUNT 8 #define PCA9546_CHANNEL_COUNT 4 -/* - * struct Pca954xChannel - The i2c mux device will have N of these states - * that own the i2c channel bus. - * @bus: The owned channel bus. - * @enabled: Is this channel active? - */ -typedef struct Pca954xChannel { - SysBusDevice parent; - - I2CBus *bus; - - bool enabled; -} Pca954xChannel; - -#define TYPE_PCA954X_CHANNEL "pca954x-channel" -#define PCA954X_CHANNEL(obj) \ - OBJECT_CHECK(Pca954xChannel, (obj), TYPE_PCA954X_CHANNEL) - /* * struct Pca954xState - The pca954x state object. * @control: The value written to the mux control. @@ -59,8 +41,8 @@ typedef struct Pca954xState { uint8_t control; - /* The channel i2c buses. */ - Pca954xChannel channel[PCA9548_CHANNEL_COUNT]; + bool enabled[PCA9548_CHANNEL_COUNT]; + I2CBus *bus[PCA9548_CHANNEL_COUNT]; } Pca954xState; /* @@ -98,11 +80,11 @@ static bool pca954x_match(I2CSlave *candidate, uint8_t address, } for (i = 0; i < mc->nchans; i++) { - if (!mux->channel[i].enabled) { + if (!mux->enabled[i]) { continue; } - if (i2c_scan_bus(mux->channel[i].bus, address, broadcast, + if (i2c_scan_bus(mux->bus[i], address, broadcast, current_devs)) { if (!broadcast) { return true; @@ -125,9 +107,9 @@ static void pca954x_enable_channel(Pca954xState *s, uint8_t enable_mask) */ for (i = 0; i < mc->nchans; i++) { if (enable_mask & (1 << i)) { - s->channel[i].enabled = true; + s->enabled[i] = true; } else { - s->channel[i].enabled = false; + s->enabled[i] = false; } } } @@ -184,23 +166,7 @@ I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel) Pca954xState *pca954x = PCA954X(mux); g_assert(channel < pc->nchans); - return I2C_BUS(qdev_get_child_bus(DEVICE(&pca954x->channel[channel]), - "i2c-bus")); -} - -static void pca954x_channel_init(Object *obj) -{ - Pca954xChannel *s = PCA954X_CHANNEL(obj); - s->bus = i2c_init_bus(DEVICE(s), "i2c-bus"); - - /* Start all channels as disabled. */ - s->enabled = false; -} - -static void pca954x_channel_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - dc->desc = "Pca954x Channel"; + return pca954x->bus[channel]; } static void pca9546_class_init(ObjectClass *klass, void *data) @@ -215,28 +181,19 @@ static void pca9548_class_init(ObjectClass *klass, void *data) s->nchans = PCA9548_CHANNEL_COUNT; } -static void pca954x_realize(DeviceState *dev, Error **errp) -{ - Pca954xState *s = PCA954X(dev); - Pca954xClass *c = PCA954X_GET_CLASS(s); - int i; - - /* SMBus modules. Cannot fail. */ - for (i = 0; i < c->nchans; i++) { - sysbus_realize(SYS_BUS_DEVICE(&s->channel[i]), &error_abort); - } -} - static void pca954x_init(Object *obj) { Pca954xState *s = PCA954X(obj); Pca954xClass *c = PCA954X_GET_CLASS(obj); int i; - /* Only initialize the children we expect. */ + /* SMBus modules. Cannot fail. */ for (i = 0; i < c->nchans; i++) { - object_initialize_child(obj, "channel[*]", &s->channel[i], - TYPE_PCA954X_CHANNEL); + g_autofree gchar *bus_name = g_strdup_printf("i2c.%d", i); + + /* start all channels as disabled. */ + s->enabled[i] = false; + s->bus[i] = i2c_init_bus(DEVICE(s), bus_name); } } @@ -252,7 +209,6 @@ static void pca954x_class_init(ObjectClass *klass, void *data) rc->phases.enter = pca954x_enter_reset; dc->desc = "Pca954x i2c-mux"; - dc->realize = pca954x_realize; k->write_data = pca954x_write_data; k->receive_byte = pca954x_read_byte; @@ -278,13 +234,6 @@ static const TypeInfo pca954x_info[] = { .parent = TYPE_PCA954X, .class_init = pca9548_class_init, }, - { - .name = TYPE_PCA954X_CHANNEL, - .parent = TYPE_SYS_BUS_DEVICE, - .class_init = pca954x_channel_class_init, - .instance_size = sizeof(Pca954xChannel), - .instance_init = pca954x_channel_init, - } }; DEFINE_TYPES(pca954x_info) diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c index b0d5c2dd74..df7313db5d 100644 --- a/hw/input/tsc210x.c +++ b/hw/input/tsc210x.c @@ -24,6 +24,7 @@ #include "hw/hw.h" #include "audio/audio.h" #include "qemu/timer.h" +#include "qemu/log.h" #include "sysemu/reset.h" #include "ui/console.h" #include "hw/arm/omap.h" /* For I2SCodec */ @@ -910,8 +911,11 @@ uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len) TSC210xState *s = opaque; uint32_t ret = 0; - if (len != 16) - hw_error("%s: FIXME: bad SPI word width %i\n", __func__, len); + if (len != 16) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad SPI word width %i\n", __func__, len); + return 0; + } /* TODO: sequential reads etc - how do we make sure the host doesn't * unintentionally read out a conversion result from a register while diff --git a/include/hw/registerfields.h b/include/hw/registerfields.h index f2a3c9c41f..3a88e135d0 100644 --- a/include/hw/registerfields.h +++ b/include/hw/registerfields.h @@ -59,6 +59,19 @@ extract64((storage), R_ ## reg ## _ ## field ## _SHIFT, \ R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_SEX8(storage, reg, field) \ + sextract8((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_SEX16(storage, reg, field) \ + sextract16((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_SEX32(storage, reg, field) \ + sextract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_SEX64(storage, reg, field) \ + sextract64((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) + /* Extract a field from an array of registers */ #define ARRAY_FIELD_EX32(regs, reg, field) \ FIELD_EX32((regs)[R_ ## reg], reg, field) @@ -95,7 +108,40 @@ _d; }) #define FIELD_DP64(storage, reg, field, val) ({ \ struct { \ - uint64_t v:R_ ## reg ## _ ## field ## _LENGTH; \ + uint64_t v:R_ ## reg ## _ ## field ## _LENGTH; \ + } _v = { .v = val }; \ + uint64_t _d; \ + _d = deposit64((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, _v.v); \ + _d; }) + +#define FIELD_SDP8(storage, reg, field, val) ({ \ + struct { \ + signed int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } _v = { .v = val }; \ + uint8_t _d; \ + _d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, _v.v); \ + _d; }) +#define FIELD_SDP16(storage, reg, field, val) ({ \ + struct { \ + signed int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } _v = { .v = val }; \ + uint16_t _d; \ + _d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, _v.v); \ + _d; }) +#define FIELD_SDP32(storage, reg, field, val) ({ \ + struct { \ + signed int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } _v = { .v = val }; \ + uint32_t _d; \ + _d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, _v.v); \ + _d; }) +#define FIELD_SDP64(storage, reg, field, val) ({ \ + struct { \ + int64_t v:R_ ## reg ## _ ## field ## _LENGTH; \ } _v = { .v = val }; \ uint64_t _d; \ _d = deposit64((storage), R_ ## reg ## _ ## field ## _SHIFT, \ diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 7f38d33b8e..b59d505761 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -10,8 +10,8 @@ #ifdef TARGET_AARCH64 # define TARGET_LONG_BITS 64 -# define TARGET_PHYS_ADDR_SPACE_BITS 48 -# define TARGET_VIRT_ADDR_SPACE_BITS 48 +# define TARGET_PHYS_ADDR_SPACE_BITS 52 +# define TARGET_VIRT_ADDR_SPACE_BITS 52 #else # define TARGET_LONG_BITS 32 # define TARGET_PHYS_ADDR_SPACE_BITS 40 diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c085dc10ee..7091684a16 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -206,10 +206,11 @@ static void arm_cpu_reset(DeviceState *dev) aarch64_sve_zcr_get_valid_len(cpu, cpu->sve_default_vq - 1); } /* + * Enable 48-bit address space (TODO: take reserved_va into account). * Enable TBI0 but not TBI1. * Note that this must match useronly_clean_ptr. */ - env->cp15.tcr_el[1].raw_tcr = (1ULL << 37); + env->cp15.tcr_el[1].raw_tcr = 5 | (1ULL << 37); /* Enable MTE */ if (cpu_isar_feature(aa64_mte, cpu)) { @@ -1110,11 +1111,12 @@ static void arm_cpu_initfn(Object *obj) * picky DTB consumer will also provide a helpful error message. */ cpu->dtb_compatible = "qemu,unknown"; - cpu->psci_version = 1; /* By default assume PSCI v0.1 */ + cpu->psci_version = QEMU_PSCI_VERSION_0_1; /* By default assume PSCI v0.1 */ cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE; if (tcg_enabled() || hvf_enabled()) { - cpu->psci_version = 2; /* TCG and HVF implement PSCI 0.2 */ + /* TCG and HVF implement PSCI 1.1 */ + cpu->psci_version = QEMU_PSCI_VERSION_1_1; } } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c6a4d50e82..24d9fff170 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -4284,11 +4284,38 @@ static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; } +static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) +{ + return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; +} + +static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); +} + +static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; +} + +static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) +{ + unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); +} + static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; } +static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; +} + static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 1171ab16b9..2fdc16bf18 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -795,7 +795,11 @@ static void aarch64_max_initfn(Object *obj) cpu->isar.id_aa64pfr1 = t; t = cpu->isar.id_aa64mmfr0; - t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 5); /* PARange: 48 bits */ + t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* FEAT_LPA: 52 bits */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* 16k pages supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* 16k stage2 supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* 64k stage2 supported */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* 4k stage2 supported */ cpu->isar.id_aa64mmfr0 = t; t = cpu->isar.id_aa64mmfr1; @@ -811,6 +815,7 @@ static void aarch64_max_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* TTCNP */ t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* TTST */ + t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ cpu->isar.id_aa64mmfr2 = t; t = cpu->isar.id_aa64zfr0; diff --git a/target/arm/helper.c b/target/arm/helper.c index 7bf50fdd76..088956eecf 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4511,70 +4511,73 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, } #ifdef TARGET_AARCH64 -static uint64_t tlbi_aa64_range_get_length(CPUARMState *env, - uint64_t value) -{ - unsigned int page_shift; - unsigned int page_size_granule; - uint64_t num; - uint64_t scale; - uint64_t exponent; +typedef struct { + uint64_t base; uint64_t length; +} TLBIRange; + +static TLBIRange tlbi_aa64_get_range(CPUARMState *env, ARMMMUIdx mmuidx, + uint64_t value) +{ + unsigned int page_size_granule, page_shift, num, scale, exponent; + /* Extract one bit to represent the va selector in use. */ + uint64_t select = sextract64(value, 36, 1); + ARMVAParameters param = aa64_va_parameters(env, select, mmuidx, true); + TLBIRange ret = { }; - num = extract64(value, 39, 5); - scale = extract64(value, 44, 2); page_size_granule = extract64(value, 46, 2); - if (page_size_granule == 0) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid page size granule %d\n", + /* The granule encoded in value must match the granule in use. */ + if (page_size_granule != (param.using64k ? 3 : param.using16k ? 2 : 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid tlbi page size granule %d\n", page_size_granule); - return 0; + return ret; } page_shift = (page_size_granule - 1) * 2 + 12; - + num = extract64(value, 39, 5); + scale = extract64(value, 44, 2); exponent = (5 * scale) + 1; - length = (num + 1) << (exponent + page_shift); - return length; -} + ret.length = (num + 1) << (exponent + page_shift); -static uint64_t tlbi_aa64_range_get_base(CPUARMState *env, uint64_t value, - bool two_ranges) -{ - /* TODO: ARMv8.7 FEAT_LPA2 */ - uint64_t pageaddr; - - if (two_ranges) { - pageaddr = sextract64(value, 0, 37) << TARGET_PAGE_BITS; + if (param.select) { + ret.base = sextract64(value, 0, 37); } else { - pageaddr = extract64(value, 0, 37) << TARGET_PAGE_BITS; + ret.base = extract64(value, 0, 37); } + if (param.ds) { + /* + * With DS=1, BaseADDR is always shifted 16 so that it is able + * to address all 52 va bits. The input address is perforce + * aligned on a 64k boundary regardless of translation granule. + */ + page_shift = 16; + } + ret.base <<= page_shift; - return pageaddr; + return ret; } static void do_rvae_write(CPUARMState *env, uint64_t value, int idxmap, bool synced) { ARMMMUIdx one_idx = ARM_MMU_IDX_A | ctz32(idxmap); - bool two_ranges = regime_has_2_ranges(one_idx); - uint64_t baseaddr, length; + TLBIRange range; int bits; - baseaddr = tlbi_aa64_range_get_base(env, value, two_ranges); - length = tlbi_aa64_range_get_length(env, value); - bits = tlbbits_for_regime(env, one_idx, baseaddr); + range = tlbi_aa64_get_range(env, one_idx, value); + bits = tlbbits_for_regime(env, one_idx, range.base); if (synced) { tlb_flush_range_by_mmuidx_all_cpus_synced(env_cpu(env), - baseaddr, - length, + range.base, + range.length, idxmap, bits); } else { - tlb_flush_range_by_mmuidx(env_cpu(env), baseaddr, - length, idxmap, bits); + tlb_flush_range_by_mmuidx(env_cpu(env), range.base, + range.length, idxmap, bits); } } @@ -6423,11 +6426,18 @@ static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = env_archcpu(env); int i = ri->crm; - /* Bits [63:49] are hardwired to the value of bit [48]; that is, the - * register reads and behaves as if values written are sign extended. + /* * Bits [1:0] are RES0. + * + * It is IMPLEMENTATION DEFINED whether [63:49] ([63:53] with FEAT_LVA) + * are hardwired to the value of bit [48] ([52] with FEAT_LVA), or if + * they contain the value written. It is CONSTRAINED UNPREDICTABLE + * whether the RESS bits are ignored when comparing an address. + * + * Therefore we are allowed to compare the entire register, which lets + * us avoid considering whether or not FEAT_LVA is actually enabled. */ - value = sextract64(value, 0, 49) & ~3ULL; + value &= ~3ULL; raw_write(env, ri, value); hw_watchpoint_update(cpu, i); @@ -6473,10 +6483,19 @@ void hw_breakpoint_update(ARMCPU *cpu, int n) case 0: /* unlinked address match */ case 1: /* linked address match */ { - /* Bits [63:49] are hardwired to the value of bit [48]; that is, - * we behave as if the register was sign extended. Bits [1:0] are - * RES0. The BAS field is used to allow setting breakpoints on 16 - * bit wide instructions; it is CONSTRAINED UNPREDICTABLE whether + /* + * Bits [1:0] are RES0. + * + * It is IMPLEMENTATION DEFINED whether bits [63:49] + * ([63:53] for FEAT_LVA) are hardwired to a copy of the sign bit + * of the VA field ([48] or [52] for FEAT_LVA), or whether the + * value is read as written. It is CONSTRAINED UNPREDICTABLE + * whether the RESS bits are ignored when comparing an address. + * Therefore we are allowed to compare the entire register, which + * lets us avoid considering whether FEAT_LVA is actually enabled. + * + * The BAS field is used to allow setting breakpoints on 16-bit + * wide instructions; it is CONSTRAINED UNPREDICTABLE whether * a bp will fire if the addresses covered by the bp and the addresses * covered by the insn overlap but the insn doesn't start at the * start of the bp address range. We choose to require the insn and @@ -6489,7 +6508,7 @@ void hw_breakpoint_update(ARMCPU *cpu, int n) * See also figure D2-3 in the v8 ARM ARM (DDI0487A.c). */ int bas = extract64(bcr, 5, 4); - addr = sextract64(bvr, 0, 49) & ~3ULL; + addr = bvr & ~3ULL; if (bas == 0) { return; } @@ -11065,13 +11084,18 @@ do_fault: * false otherwise. */ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, - int inputsize, int stride) + int inputsize, int stride, int outputsize) { const int grainsize = stride + 3; int startsizecheck; - /* Negative levels are never allowed. */ - if (level < 0) { + /* + * Negative levels are usually not allowed... + * Except for FEAT_LPA2, 4k page table, 52-bit address space, which + * begins with level -1. Note that previous feature tests will have + * eliminated this combination if it is not enabled. + */ + if (level < (inputsize == 52 && stride == 9 ? -1 : 0)) { return false; } @@ -11081,22 +11105,19 @@ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, } if (is_aa64) { - CPUARMState *env = &cpu->env; - unsigned int pamax = arm_pamax(cpu); - switch (stride) { case 13: /* 64KB Pages. */ - if (level == 0 || (level == 1 && pamax <= 42)) { + if (level == 0 || (level == 1 && outputsize <= 42)) { return false; } break; case 11: /* 16KB Pages. */ - if (level == 0 || (level == 1 && pamax <= 40)) { + if (level == 0 || (level == 1 && outputsize <= 40)) { return false; } break; case 9: /* 4KB Pages. */ - if (level == 0 && pamax <= 42) { + if (level == 0 && outputsize <= 42) { return false; } break; @@ -11105,8 +11126,8 @@ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, } /* Inputsize checks. */ - if (inputsize > pamax && - (arm_el_is_aa64(env, 1) || inputsize > 40)) { + if (inputsize > outputsize && + (arm_el_is_aa64(&cpu->env, 1) || inputsize > 40)) { /* This is CONSTRAINED UNPREDICTABLE and we choose to fault. */ return false; } @@ -11152,6 +11173,31 @@ static uint8_t convert_stage2_attrs(CPUARMState *env, uint8_t s2attrs) } #endif /* !CONFIG_USER_ONLY */ +/* This mapping is common between ID_AA64MMFR0.PARANGE and TCR_ELx.{I}PS. */ +static const uint8_t pamax_map[] = { + [0] = 32, + [1] = 36, + [2] = 40, + [3] = 42, + [4] = 44, + [5] = 48, + [6] = 52, +}; + +/* The cpu-specific constant value of PAMax; also used by hw/arm/virt. */ +unsigned int arm_pamax(ARMCPU *cpu) +{ + unsigned int parange = + FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + + /* + * id_aa64mmfr0 is a read-only register so values outside of the + * supported mappings can be considered an implementation error. + */ + assert(parange < ARRAY_SIZE(pamax_map)); + return pamax_map[parange]; +} + static int aa64_va_parameter_tbi(uint64_t tcr, ARMMMUIdx mmu_idx) { if (regime_has_2_ranges(mmu_idx)) { @@ -11190,8 +11236,9 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, ARMMMUIdx mmu_idx, bool data) { uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; - bool epd, hpd, using16k, using64k; - int select, tsz, tbi, max_tsz; + bool epd, hpd, using16k, using64k, tsz_oob, ds; + int select, tsz, tbi, max_tsz, min_tsz, ps, sh; + ARMCPU *cpu = env_archcpu(env); if (!regime_has_2_ranges(mmu_idx)) { select = 0; @@ -11205,6 +11252,9 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, hpd = extract32(tcr, 24, 1); } epd = false; + sh = extract32(tcr, 12, 2); + ps = extract32(tcr, 16, 3); + ds = extract64(tcr, 32, 1); } else { /* * Bit 55 is always between the two regions, and is canonical for @@ -11214,6 +11264,7 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, if (!select) { tsz = extract32(tcr, 0, 6); epd = extract32(tcr, 7, 1); + sh = extract32(tcr, 12, 2); using64k = extract32(tcr, 14, 1); using16k = extract32(tcr, 15, 1); hpd = extract64(tcr, 41, 1); @@ -11223,18 +11274,61 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, using64k = tg == 3; tsz = extract32(tcr, 16, 6); epd = extract32(tcr, 23, 1); + sh = extract32(tcr, 28, 2); hpd = extract64(tcr, 42, 1); } + ps = extract64(tcr, 32, 3); + ds = extract64(tcr, 59, 1); } - if (cpu_isar_feature(aa64_st, env_archcpu(env))) { + if (cpu_isar_feature(aa64_st, cpu)) { max_tsz = 48 - using64k; } else { max_tsz = 39; } - tsz = MIN(tsz, max_tsz); - tsz = MAX(tsz, 16); /* TODO: ARMv8.2-LVA */ + /* + * DS is RES0 unless FEAT_LPA2 is supported for the given page size; + * adjust the effective value of DS, as documented. + */ + min_tsz = 16; + if (using64k) { + if (cpu_isar_feature(aa64_lva, cpu)) { + min_tsz = 12; + } + ds = false; + } else if (ds) { + switch (mmu_idx) { + case ARMMMUIdx_Stage2: + case ARMMMUIdx_Stage2_S: + if (using16k) { + ds = cpu_isar_feature(aa64_tgran16_2_lpa2, cpu); + } else { + ds = cpu_isar_feature(aa64_tgran4_2_lpa2, cpu); + } + break; + default: + if (using16k) { + ds = cpu_isar_feature(aa64_tgran16_lpa2, cpu); + } else { + ds = cpu_isar_feature(aa64_tgran4_lpa2, cpu); + } + break; + } + if (ds) { + min_tsz = 12; + } + } + + if (tsz > max_tsz) { + tsz = max_tsz; + tsz_oob = true; + } else if (tsz < min_tsz) { + tsz = min_tsz; + tsz_oob = true; + } else { + tsz_oob = false; + } /* Present TBI as a composite with TBID. */ tbi = aa64_va_parameter_tbi(tcr, mmu_idx); @@ -11245,12 +11339,16 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, return (ARMVAParameters) { .tsz = tsz, + .ps = ps, + .sh = sh, .select = select, .tbi = tbi, .epd = epd, .hpd = hpd, .using16k = using16k, .using64k = using64k, + .tsz_oob = tsz_oob, + .ds = ds, }; } @@ -11361,7 +11459,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, target_ulong page_size; uint32_t attrs; int32_t stride; - int addrsize, inputsize; + int addrsize, inputsize, outputsize; TCR *tcr = regime_tcr(env, mmu_idx); int ap, ns, xn, pxn; uint32_t el = regime_el(env, mmu_idx); @@ -11371,16 +11469,44 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, /* TODO: This code does not support shareability levels. */ if (aarch64) { + int ps; + param = aa64_va_parameters(env, address, mmu_idx, access_type != MMU_INST_FETCH); level = 0; + + /* + * If TxSZ is programmed to a value larger than the maximum, + * or smaller than the effective minimum, it is IMPLEMENTATION + * DEFINED whether we behave as if the field were programmed + * within bounds, or if a level 0 Translation fault is generated. + * + * With FEAT_LVA, fault on less than minimum becomes required, + * so our choice is to always raise the fault. + */ + if (param.tsz_oob) { + fault_type = ARMFault_Translation; + goto do_fault; + } + addrsize = 64 - 8 * param.tbi; inputsize = 64 - param.tsz; + + /* + * Bound PS by PARANGE to find the effective output address size. + * ID_AA64MMFR0 is a read-only register so values outside of the + * supported mappings can be considered an implementation error. + */ + ps = FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + ps = MIN(ps, param.ps); + assert(ps < ARRAY_SIZE(pamax_map)); + outputsize = pamax_map[ps]; } else { param = aa32_va_parameters(env, address, mmu_idx); level = 1; addrsize = (mmu_idx == ARMMMUIdx_Stage2 ? 40 : 32); inputsize = addrsize - param.tsz; + outputsize = 40; } /* @@ -11448,10 +11574,19 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, * VTCR_EL2.SL0 field (whose interpretation depends on the page size) */ uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2); + uint32_t sl2 = extract64(tcr->raw_tcr, 33, 1); uint32_t startlevel; bool ok; - if (!aarch64 || stride == 9) { + /* SL2 is RES0 unless DS=1 & 4kb granule. */ + if (param.ds && stride == 9 && sl2) { + if (sl0 != 0) { + level = 0; + fault_type = ARMFault_Translation; + goto do_fault; + } + startlevel = -1; + } else if (!aarch64 || stride == 9) { /* AArch32 or 4KB pages */ startlevel = 2 - sl0; @@ -11465,7 +11600,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, /* Check that the starting level is valid. */ ok = check_s2_mmu_setup(cpu, aarch64, startlevel, - inputsize, stride); + inputsize, stride, outputsize); if (!ok) { fault_type = ARMFault_Translation; goto do_fault; @@ -11473,24 +11608,49 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, level = startlevel; } - indexmask_grainsize = (1ULL << (stride + 3)) - 1; - indexmask = (1ULL << (inputsize - (stride * (4 - level)))) - 1; + indexmask_grainsize = MAKE_64BIT_MASK(0, stride + 3); + indexmask = MAKE_64BIT_MASK(0, inputsize - (stride * (4 - level))); /* Now we can extract the actual base address from the TTBR */ descaddr = extract64(ttbr, 0, 48); + + /* + * For FEAT_LPA and PS=6, bits [51:48] of descaddr are in [5:2] of TTBR. + * + * Otherwise, if the base address is out of range, raise AddressSizeFault. + * In the pseudocode, this is !IsZero(baseregister<47:outputsize>), + * but we've just cleared the bits above 47, so simplify the test. + */ + if (outputsize > 48) { + descaddr |= extract64(ttbr, 2, 4) << 48; + } else if (descaddr >> outputsize) { + level = 0; + fault_type = ARMFault_AddressSize; + goto do_fault; + } + /* * We rely on this masking to clear the RES0 bits at the bottom of the TTBR * and also to mask out CnP (bit 0) which could validly be non-zero. */ descaddr &= ~indexmask; - /* The address field in the descriptor goes up to bit 39 for ARMv7 - * but up to bit 47 for ARMv8, but we use the descaddrmask - * up to bit 39 for AArch32, because we don't need other bits in that case - * to construct next descriptor address (anyway they should be all zeroes). + /* + * For AArch32, the address field in the descriptor goes up to bit 39 + * for both v7 and v8. However, for v8 the SBZ bits [47:40] must be 0 + * or an AddressSize fault is raised. So for v8 we extract those SBZ + * bits as part of the address, which will be checked via outputsize. + * For AArch64, the address field goes up to bit 47, or 49 with FEAT_LPA2; + * the highest bits of a 52-bit output are placed elsewhere. */ - descaddrmask = ((1ull << (aarch64 ? 48 : 40)) - 1) & - ~indexmask_grainsize; + if (param.ds) { + descaddrmask = MAKE_64BIT_MASK(0, 50); + } else if (arm_feature(env, ARM_FEATURE_V8)) { + descaddrmask = MAKE_64BIT_MASK(0, 48); + } else { + descaddrmask = MAKE_64BIT_MASK(0, 40); + } + descaddrmask &= ~indexmask_grainsize; /* Secure accesses start with the page table in secure memory and * can be downgraded to non-secure at any step. Non-secure accesses @@ -11515,8 +11675,26 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, /* Invalid, or the Reserved level 3 encoding */ goto do_fault; } + descaddr = descriptor & descaddrmask; + /* + * For FEAT_LPA and PS=6, bits [51:48] of descaddr are in [15:12] + * of descriptor. For FEAT_LPA2 and effective DS, bits [51:50] of + * descaddr are in [9:8]. Otherwise, if descaddr is out of range, + * raise AddressSizeFault. + */ + if (outputsize > 48) { + if (param.ds) { + descaddr |= extract64(descriptor, 8, 2) << 50; + } else { + descaddr |= extract64(descriptor, 12, 4) << 48; + } + } else if (descaddr >> outputsize) { + fault_type = ARMFault_AddressSize; + goto do_fault; + } + if ((descriptor & 2) && (level < 3)) { /* Table entry. The top five bits are attributes which may * propagate down through lower levels of the table (and @@ -11605,7 +11783,17 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address, assert(attrindx <= 7); cacheattrs->attrs = extract64(mair, attrindx * 8, 8); } - cacheattrs->shareability = extract32(attrs, 6, 2); + + /* + * For FEAT_LPA2 and effective DS, the SH field in the attributes + * was re-purposed for output address bits. The SH attribute in + * that case comes from TCR_ELx, which we extracted earlier. + */ + if (param.ds) { + cacheattrs->shareability = param.sh; + } else { + cacheattrs->shareability = extract32(attrs, 6, 2); + } *phys_ptr = descaddr; *page_size_ptr = page_size; diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 4d4ddab348..8c34f86792 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -678,7 +678,7 @@ static bool hvf_handle_psci_call(CPUState *cpu) switch (param[0]) { case QEMU_PSCI_0_2_FN_PSCI_VERSION: - ret = QEMU_PSCI_0_2_RET_VERSION_0_2; + ret = QEMU_PSCI_VERSION_1_1; break; case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */ @@ -746,6 +746,31 @@ static bool hvf_handle_psci_call(CPUState *cpu) case QEMU_PSCI_0_2_FN_MIGRATE: ret = QEMU_PSCI_RET_NOT_SUPPORTED; break; + case QEMU_PSCI_1_0_FN_PSCI_FEATURES: + switch (param[1]) { + case QEMU_PSCI_0_2_FN_PSCI_VERSION: + case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: + case QEMU_PSCI_0_2_FN_AFFINITY_INFO: + case QEMU_PSCI_0_2_FN64_AFFINITY_INFO: + case QEMU_PSCI_0_2_FN_SYSTEM_RESET: + case QEMU_PSCI_0_2_FN_SYSTEM_OFF: + case QEMU_PSCI_0_1_FN_CPU_ON: + case QEMU_PSCI_0_2_FN_CPU_ON: + case QEMU_PSCI_0_2_FN64_CPU_ON: + case QEMU_PSCI_0_1_FN_CPU_OFF: + case QEMU_PSCI_0_2_FN_CPU_OFF: + case QEMU_PSCI_0_1_FN_CPU_SUSPEND: + case QEMU_PSCI_0_2_FN_CPU_SUSPEND: + case QEMU_PSCI_0_2_FN64_CPU_SUSPEND: + case QEMU_PSCI_1_0_FN_PSCI_FEATURES: + ret = 0; + break; + case QEMU_PSCI_0_1_FN_MIGRATE: + case QEMU_PSCI_0_2_FN_MIGRATE: + default: + ret = QEMU_PSCI_RET_NOT_SUPPORTED; + } + break; default: return false; } diff --git a/target/arm/internals.h b/target/arm/internals.h index 3f05748ea4..a34be2e459 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -243,24 +243,7 @@ static inline void update_spsel(CPUARMState *env, uint32_t imm) * Returns the implementation defined bit-width of physical addresses. * The ARMv8 reference manuals refer to this as PAMax(). */ -static inline unsigned int arm_pamax(ARMCPU *cpu) -{ - static const unsigned int pamax_map[] = { - [0] = 32, - [1] = 36, - [2] = 40, - [3] = 42, - [4] = 44, - [5] = 48, - }; - unsigned int parange = - FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); - - /* id_aa64mmfr0 is a read-only register so values outside of the - * supported mappings can be considered an implementation error. */ - assert(parange < ARRAY_SIZE(pamax_map)); - return pamax_map[parange]; -} +unsigned int arm_pamax(ARMCPU *cpu); /* Return true if extended addresses are enabled. * This is always the case if our translation regime is 64 bit, @@ -479,28 +462,51 @@ static inline uint32_t arm_fi_to_lfsc(ARMMMUFaultInfo *fi) case ARMFault_None: return 0; case ARMFault_AddressSize: - fsc = fi->level & 3; + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b101001; + } else { + fsc = fi->level; + } break; case ARMFault_AccessFlag: - fsc = (fi->level & 3) | (0x2 << 2); + assert(fi->level >= 0 && fi->level <= 3); + fsc = 0b001000 | fi->level; break; case ARMFault_Permission: - fsc = (fi->level & 3) | (0x3 << 2); + assert(fi->level >= 0 && fi->level <= 3); + fsc = 0b001100 | fi->level; break; case ARMFault_Translation: - fsc = (fi->level & 3) | (0x1 << 2); + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b101011; + } else { + fsc = 0b000100 | fi->level; + } break; case ARMFault_SyncExternal: fsc = 0x10 | (fi->ea << 12); break; case ARMFault_SyncExternalOnWalk: - fsc = (fi->level & 3) | (0x5 << 2) | (fi->ea << 12); + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b010011; + } else { + fsc = 0b010100 | fi->level; + } + fsc |= fi->ea << 12; break; case ARMFault_SyncParity: fsc = 0x18; break; case ARMFault_SyncParityOnWalk: - fsc = (fi->level & 3) | (0x7 << 2); + assert(fi->level >= -1 && fi->level <= 3); + if (fi->level < 0) { + fsc = 0b011011; + } else { + fsc = 0b011100 | fi->level; + } break; case ARMFault_AsyncParity: fsc = 0x19; @@ -1049,12 +1055,16 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id) */ typedef struct ARMVAParameters { unsigned tsz : 8; + unsigned ps : 3; + unsigned sh : 2; unsigned select : 1; bool tbi : 1; bool epd : 1; bool hpd : 1; bool using16k : 1; bool using64k : 1; + bool tsz_oob : 1; /* tsz has been clamped to legal range */ + bool ds : 1; } ARMVAParameters; ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index 580f1c1fee..faacf96fdc 100644 --- a/target/arm/kvm-consts.h +++ b/target/arm/kvm-consts.h @@ -77,6 +77,8 @@ MISMATCH_CHECK(QEMU_PSCI_0_1_FN_MIGRATE, KVM_PSCI_FN_MIGRATE); #define QEMU_PSCI_0_2_FN64_AFFINITY_INFO QEMU_PSCI_0_2_FN64(4) #define QEMU_PSCI_0_2_FN64_MIGRATE QEMU_PSCI_0_2_FN64(5) +#define QEMU_PSCI_1_0_FN_PSCI_FEATURES QEMU_PSCI_0_2_FN(10) + MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_SUSPEND, PSCI_0_2_FN_CPU_SUSPEND); MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_OFF, PSCI_0_2_FN_CPU_OFF); MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_ON, PSCI_0_2_FN_CPU_ON); @@ -84,18 +86,22 @@ MISMATCH_CHECK(QEMU_PSCI_0_2_FN_MIGRATE, PSCI_0_2_FN_MIGRATE); MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_SUSPEND, PSCI_0_2_FN64_CPU_SUSPEND); MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_CPU_ON, PSCI_0_2_FN64_CPU_ON); MISMATCH_CHECK(QEMU_PSCI_0_2_FN64_MIGRATE, PSCI_0_2_FN64_MIGRATE); +MISMATCH_CHECK(QEMU_PSCI_1_0_FN_PSCI_FEATURES, PSCI_1_0_FN_PSCI_FEATURES); /* PSCI v0.2 return values used by TCG emulation of PSCI */ /* No Trusted OS migration to worry about when offlining CPUs */ #define QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED 2 -/* We implement version 0.2 only */ -#define QEMU_PSCI_0_2_RET_VERSION_0_2 2 +#define QEMU_PSCI_VERSION_0_1 0x00001 +#define QEMU_PSCI_VERSION_0_2 0x00002 +#define QEMU_PSCI_VERSION_1_0 0x10000 +#define QEMU_PSCI_VERSION_1_1 0x10001 MISMATCH_CHECK(QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED, PSCI_0_2_TOS_MP); -MISMATCH_CHECK(QEMU_PSCI_0_2_RET_VERSION_0_2, - (PSCI_VERSION_MAJOR(0) | PSCI_VERSION_MINOR(2))); +/* We don't bother to check every possible version value */ +MISMATCH_CHECK(QEMU_PSCI_VERSION_0_2, PSCI_VERSION(0, 2)); +MISMATCH_CHECK(QEMU_PSCI_VERSION_1_1, PSCI_VERSION(1, 1)); /* PSCI return values (inclusive of all PSCI versions) */ #define QEMU_PSCI_RET_SUCCESS 0 diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 71c3ca6971..ccadfbbe72 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -849,6 +849,7 @@ int kvm_arch_init_vcpu(CPUState *cs) uint64_t mpidr; ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; + uint64_t psciver; if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || !object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU)) { @@ -864,7 +865,7 @@ int kvm_arch_init_vcpu(CPUState *cs) cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF; } if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) { - cpu->psci_version = 2; + cpu->psci_version = QEMU_PSCI_VERSION_0_2; cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2; } if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { @@ -904,6 +905,17 @@ int kvm_arch_init_vcpu(CPUState *cs) } } + /* + * KVM reports the exact PSCI version it is implementing via a + * special sysreg. If it is present, use its contents to determine + * what to report to the guest in the dtb (it is the PSCI version, + * in the same 15-bits major 16-bits minor format that PSCI_VERSION + * returns). + */ + if (!kvm_get_one_reg(cs, KVM_REG_ARM_PSCI_VERSION, &psciver)) { + cpu->psci_version = psciver; + } + /* * When KVM is in use, PSCI is emulated in-kernel and not by qemu. * Currently KVM has its own idea about MPIDR assignment, so we diff --git a/target/arm/psci.c b/target/arm/psci.c index b279c0b9a4..6c1239bb96 100644 --- a/target/arm/psci.c +++ b/target/arm/psci.c @@ -57,7 +57,7 @@ void arm_handle_psci_call(ARMCPU *cpu) { /* * This function partially implements the logic for dispatching Power State - * Coordination Interface (PSCI) calls (as described in ARM DEN 0022B.b), + * Coordination Interface (PSCI) calls (as described in ARM DEN 0022D.b), * to the extent required for bringing up and taking down secondary cores, * and for handling reset and poweroff requests. * Additional information about the calling convention used is available in @@ -80,7 +80,7 @@ void arm_handle_psci_call(ARMCPU *cpu) } if ((param[0] & QEMU_PSCI_0_2_64BIT) && !is_a64(env)) { - ret = QEMU_PSCI_RET_INVALID_PARAMS; + ret = QEMU_PSCI_RET_NOT_SUPPORTED; goto err; } @@ -89,7 +89,7 @@ void arm_handle_psci_call(ARMCPU *cpu) ARMCPU *target_cpu; case QEMU_PSCI_0_2_FN_PSCI_VERSION: - ret = QEMU_PSCI_0_2_RET_VERSION_0_2; + ret = QEMU_PSCI_VERSION_1_1; break; case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */ @@ -170,6 +170,35 @@ void arm_handle_psci_call(ARMCPU *cpu) } helper_wfi(env, 4); break; + case QEMU_PSCI_1_0_FN_PSCI_FEATURES: + switch (param[1]) { + case QEMU_PSCI_0_2_FN_PSCI_VERSION: + case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: + case QEMU_PSCI_0_2_FN_AFFINITY_INFO: + case QEMU_PSCI_0_2_FN64_AFFINITY_INFO: + case QEMU_PSCI_0_2_FN_SYSTEM_RESET: + case QEMU_PSCI_0_2_FN_SYSTEM_OFF: + case QEMU_PSCI_0_1_FN_CPU_ON: + case QEMU_PSCI_0_2_FN_CPU_ON: + case QEMU_PSCI_0_2_FN64_CPU_ON: + case QEMU_PSCI_0_1_FN_CPU_OFF: + case QEMU_PSCI_0_2_FN_CPU_OFF: + case QEMU_PSCI_0_1_FN_CPU_SUSPEND: + case QEMU_PSCI_0_2_FN_CPU_SUSPEND: + case QEMU_PSCI_0_2_FN64_CPU_SUSPEND: + case QEMU_PSCI_1_0_FN_PSCI_FEATURES: + if (!(param[1] & QEMU_PSCI_0_2_64BIT) || is_a64(env)) { + ret = 0; + break; + } + /* fallthrough */ + case QEMU_PSCI_0_1_FN_MIGRATE: + case QEMU_PSCI_0_2_FN_MIGRATE: + default: + ret = QEMU_PSCI_RET_NOT_SUPPORTED; + break; + } + break; case QEMU_PSCI_0_1_FN_MIGRATE: case QEMU_PSCI_0_2_FN_MIGRATE: default: diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 5a1df25f91..d1a59fad9c 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -9045,9 +9045,9 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, } } - tcg_temp_free_ptr(tcg_fpstatus); tcg_temp_free_i32(tcg_shift); gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + tcg_temp_free_ptr(tcg_fpstatus); tcg_temp_free_i32(tcg_rmode); } diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index f33d84d19b..721eafad12 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -190,6 +190,7 @@ qtests_npcm7xx = \ 'npcm7xx_gpio-test', 'npcm7xx_pwm-test', 'npcm7xx_rng-test', + 'npcm7xx_sdhci-test', 'npcm7xx_smbus-test', 'npcm7xx_timer-test', 'npcm7xx_watchdog_timer-test'] + \ diff --git a/tests/qtest/npcm7xx_sdhci-test.c b/tests/qtest/npcm7xx_sdhci-test.c new file mode 100644 index 0000000000..c1f496fb29 --- /dev/null +++ b/tests/qtest/npcm7xx_sdhci-test.c @@ -0,0 +1,215 @@ +/* + * QTests for NPCM7xx SD-3.0 / MMC-4.51 Host Controller + * + * Copyright (c) 2022 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "hw/sd/npcm7xx_sdhci.h" + +#include "libqos/libqtest.h" +#include "libqtest-single.h" +#include "libqos/sdhci-cmd.h" + +#define NPCM7XX_REG_SIZE 0x100 +#define NPCM7XX_MMC_BA 0xF0842000 +#define NPCM7XX_BLK_SIZE 512 +#define NPCM7XX_TEST_IMAGE_SIZE (1 << 30) + +char *sd_path; + +static QTestState *setup_sd_card(void) +{ + QTestState *qts = qtest_initf( + "-machine kudo-bmc " + "-device sd-card,drive=drive0 " + "-drive id=drive0,if=none,file=%s,format=raw,auto-read-only=off", + sd_path); + + qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_SWRST, SDHC_RESET_ALL); + qtest_writew(qts, NPCM7XX_MMC_BA + SDHC_CLKCON, + SDHC_CLOCK_SDCLK_EN | SDHC_CLOCK_INT_STABLE | + SDHC_CLOCK_INT_EN); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8)); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR); + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x45670000, 0, + SDHC_SELECT_DESELECT_CARD); + + return qts; +} + +static void write_sdread(QTestState *qts, const char *msg) +{ + int fd, ret; + size_t len = strlen(msg); + char *rmsg = g_malloc(len); + + /* write message to sd */ + fd = open(sd_path, O_WRONLY); + g_assert(fd >= 0); + ret = write(fd, msg, len); + close(fd); + g_assert(ret == len); + + /* read message using sdhci */ + ret = sdhci_read_cmd(qts, NPCM7XX_MMC_BA, rmsg, len); + g_assert(ret == len); + g_assert(!memcmp(rmsg, msg, len)); + + g_free(rmsg); +} + +/* Check MMC can read values from sd */ +static void test_read_sd(void) +{ + QTestState *qts = setup_sd_card(); + + write_sdread(qts, "hello world"); + write_sdread(qts, "goodbye"); + + qtest_quit(qts); +} + +static void sdwrite_read(QTestState *qts, const char *msg) +{ + int fd, ret; + size_t len = strlen(msg); + char *rmsg = g_malloc(len); + + /* write message using sdhci */ + sdhci_write_cmd(qts, NPCM7XX_MMC_BA, msg, len, NPCM7XX_BLK_SIZE); + + /* read message from sd */ + fd = open(sd_path, O_RDONLY); + g_assert(fd >= 0); + ret = read(fd, rmsg, len); + close(fd); + g_assert(ret == len); + + g_assert(!memcmp(rmsg, msg, len)); + + g_free(rmsg); +} + +/* Check MMC can write values to sd */ +static void test_write_sd(void) +{ + QTestState *qts = setup_sd_card(); + + sdwrite_read(qts, "hello world"); + sdwrite_read(qts, "goodbye"); + + qtest_quit(qts); +} + +/* Check SDHCI has correct default values. */ +static void test_reset(void) +{ + QTestState *qts = qtest_init("-machine kudo-bmc"); + uint64_t addr = NPCM7XX_MMC_BA; + uint64_t end_addr = addr + NPCM7XX_REG_SIZE; + uint16_t prstvals_resets[] = {NPCM7XX_PRSTVALS_0_RESET, + NPCM7XX_PRSTVALS_1_RESET, + 0, + NPCM7XX_PRSTVALS_3_RESET, + 0, + 0}; + int i; + uint32_t mask; + + while (addr < end_addr) { + switch (addr - NPCM7XX_MMC_BA) { + case SDHC_PRNSTS: + /* + * ignores bits 20 to 24: they are changed when reading registers + */ + mask = 0x1f00000; + g_assert_cmphex(qtest_readl(qts, addr) | mask, ==, + NPCM7XX_PRSNTS_RESET | mask); + addr += 4; + break; + case SDHC_BLKGAP: + g_assert_cmphex(qtest_readb(qts, addr), ==, NPCM7XX_BLKGAP_RESET); + addr += 1; + break; + case SDHC_CAPAB: + g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_CAPAB_RESET); + addr += 8; + break; + case SDHC_MAXCURR: + g_assert_cmphex(qtest_readq(qts, addr), ==, NPCM7XX_MAXCURR_RESET); + addr += 8; + break; + case SDHC_HCVER: + g_assert_cmphex(qtest_readw(qts, addr), ==, NPCM7XX_HCVER_RESET); + addr += 2; + break; + case NPCM7XX_PRSTVALS: + for (i = 0; i < NPCM7XX_PRSTVALS_SIZE; ++i) { + g_assert_cmphex(qtest_readw(qts, addr + 2 * i), ==, + prstvals_resets[i]); + } + addr += NPCM7XX_PRSTVALS_SIZE * 2; + break; + default: + g_assert_cmphex(qtest_readb(qts, addr), ==, 0); + addr += 1; + } + } + + qtest_quit(qts); +} + +static void drive_destroy(void) +{ + unlink(sd_path); + g_free(sd_path); +} + +static void drive_create(void) +{ + int fd, ret; + GError *error = NULL; + + /* Create a temporary raw image */ + fd = g_file_open_tmp("sdhci_XXXXXX", &sd_path, &error); + if (fd == -1) { + fprintf(stderr, "unable to create sdhci file: %s\n", error->message); + g_error_free(error); + } + g_assert(sd_path != NULL); + + ret = ftruncate(fd, NPCM7XX_TEST_IMAGE_SIZE); + g_assert_cmpint(ret, ==, 0); + g_message("%s", sd_path); + close(fd); +} + +int main(int argc, char **argv) +{ + int ret; + + drive_create(); + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("npcm7xx_sdhci/reset", test_reset); + qtest_add_func("npcm7xx_sdhci/write_sd", test_write_sd); + qtest_add_func("npcm7xx_sdhci/read_sd", test_read_sd); + + ret = g_test_run(); + drive_destroy(); + return ret; +} diff --git a/ui/cocoa.m b/ui/cocoa.m index a8f1cdaf92..b6e70e9134 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -522,8 +522,9 @@ QemuCocoaView *cocoaView; } } -- (void) updateUIInfo +- (void) updateUIInfoLocked { + /* Must be called with the iothread lock, i.e. via updateUIInfo */ NSSize frameSize; QemuUIInfo info; @@ -554,6 +555,25 @@ QemuCocoaView *cocoaView; dpy_set_ui_info(dcl.con, &info, TRUE); } +- (void) updateUIInfo +{ + if (!allow_events) { + /* + * Don't try to tell QEMU about UI information in the application + * startup phase -- we haven't yet registered dcl with the QEMU UI + * layer, and also trying to take the iothread lock would deadlock. + * When cocoa_display_init() does register the dcl, the UI layer + * will call cocoa_switch(), which will call updateUIInfo, so + * we don't lose any information here. + */ + return; + } + + with_iothread_lock(^{ + [self updateUIInfoLocked]; + }); +} + - (void)viewDidMoveToWindow { [self updateUIInfo]; @@ -1956,8 +1976,6 @@ int main (int argc, char **argv) { static void cocoa_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); dispatch_async(dispatch_get_main_queue(), ^{ @@ -1973,20 +1991,15 @@ static void cocoa_update(DisplayChangeListener *dcl, } [cocoaView setNeedsDisplayInRect:rect]; }); - - [pool release]; } static void cocoa_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; pixman_image_t *image = surface->image; COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); - [cocoaView updateUIInfo]; - // The DisplaySurface will be freed as soon as this callback returns. // We take a reference to the underlying pixman image here so it does // not disappear from under our feet; the switchSurface method will @@ -1994,9 +2007,9 @@ static void cocoa_switch(DisplayChangeListener *dcl, pixman_image_ref(image); dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView updateUIInfo]; [cocoaView switchSurface:image]; }); - [pool release]; } static void cocoa_refresh(DisplayChangeListener *dcl)