target-arm queue:
* mps3-an547: Add missing user ahb interfaces * hw/arm/mps2-tz.c: Update AN547 documentation URL * hw/input/tsc210x: Don't abort on bad SPI word widths * hw/i2c: flatten pca954x mux device * target/arm: Support PSCI 1.1 and SMCCC 1.0 * target/arm: Fix early free of TCG temp in handle_simd_shift_fpint_conv() * tests/qtest: add qtests for npcm7xx sdhci * Implement FEAT_LVA * Implement FEAT_LPA * Implement FEAT_LPA2 (but do not enable it yet) * Report KVM's actual PSCI version to guest in dtb * ui/cocoa.m: Fix updateUIInfo threading issues * ui/cocoa.m: Remove unnecessary NSAutoreleasePools -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmIf2PYZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3jPkD/91FqRiubeyDEuuV77YMN3y AXA0He8FJ4TgfCZ4e9IT2rA5lyYvFdx0c6TUmFKc3JJ2omhP+35sT+7qolsuhKhF JHIvULVMakazbna4tanuQN6fTPrdhnUtVdXDTbadvdjzyWtkiMSs/Mn6YT5kmUQ+ VRrks4GjB3Ckrne/45nNO5fnt9cTP1+36+1nRUk0/4SdK7BnMR332KRMj6NexVdT Ae3usp8wcJuwojCiDqkN6ov7LdbjLxKFl2tzyjfHHaOAjBJ4FgyLsR66QtY6BH+8 O8Uelx6/AYZ8o/CLqgimcG1ZQm8onGktKhMdg95TAWhRHPMWX0BXtVq1nYWt1e2+ O7ONbJdKYG6V7Qm9+cu8H7kzW7LU+iBabZ3dL3H5cpciuGWtvPT2ASfqTGKUbbyS gyC5esy4m4pw+saebMKfLcuNr7DbB+0f7OQnprmfTTecBUdHwtT8s1TRXZF0glQS zMzUcbMMWYakdNA+iPH/xRax3+S359W5oq3Q2yqHnrcyNj4kgYKHnhD8IPIsVUQo IJwC/T6OwQlUP1s/EdMI1FV63Wz7RTzsUVcVjkQGJOOeA2vOLszm/AUtDDgJhn38 /J6fN6drkxr69X9H/EjJIRGB4VL13lk/TA1zBTnxr46R2TnI8aLxHrh8G9Ruj2zN kosJ1Le5oyzDgz/6xZ0biQ== =CUTQ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20220302' into staging target-arm queue: * mps3-an547: Add missing user ahb interfaces * hw/arm/mps2-tz.c: Update AN547 documentation URL * hw/input/tsc210x: Don't abort on bad SPI word widths * hw/i2c: flatten pca954x mux device * target/arm: Support PSCI 1.1 and SMCCC 1.0 * target/arm: Fix early free of TCG temp in handle_simd_shift_fpint_conv() * tests/qtest: add qtests for npcm7xx sdhci * Implement FEAT_LVA * Implement FEAT_LPA * Implement FEAT_LPA2 (but do not enable it yet) * Report KVM's actual PSCI version to guest in dtb * ui/cocoa.m: Fix updateUIInfo threading issues * ui/cocoa.m: Remove unnecessary NSAutoreleasePools # gpg: Signature made Wed 02 Mar 2022 20:52:06 GMT # 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-20220302: (26 commits) ui/cocoa.m: Remove unnecessary NSAutoreleasePools ui/cocoa.m: Fix updateUIInfo threading issues target/arm: Report KVM's actual PSCI version to guest in dtb target/arm: Implement FEAT_LPA2 target/arm: Advertise all page sizes for -cpu max target/arm: Validate tlbi TG matches translation granule in use target/arm: Fix TLBIRange.base for 16k and 64k pages target/arm: Introduce tlbi_aa64_get_range target/arm: Extend arm_fi_to_lfsc to level -1 target/arm: Implement FEAT_LPA target/arm: Implement FEAT_LVA target/arm: Prepare DBGBVR and DBGWVR for FEAT_LVA target/arm: Honor TCR_ELx.{I}PS target/arm: Use MAKE_64BIT_MASK to compute indexmask target/arm: Pass outputsize down to check_s2_mmu_setup target/arm: Move arm_pamax out of line target/arm: Fault on invalid TCR_ELx.TxSZ target/arm: Set TCR_EL1.TSZ for user-only hw/registerfields: Add FIELD_SEX<N> and FIELD_SDP<N> tests/qtest: add qtests for npcm7xx sdhci ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
6629bf78aa
|
@ -24,9 +24,12 @@ the following architecture extensions:
|
||||||
- FEAT_I8MM (AArch64 Int8 matrix multiplication instructions)
|
- FEAT_I8MM (AArch64 Int8 matrix multiplication instructions)
|
||||||
- FEAT_JSCVT (JavaScript conversion instructions)
|
- FEAT_JSCVT (JavaScript conversion instructions)
|
||||||
- FEAT_LOR (Limited ordering regions)
|
- 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_LRCPC (Load-acquire RCpc instructions)
|
||||||
- FEAT_LRCPC2 (Load-acquire RCpc instructions v2)
|
- FEAT_LRCPC2 (Load-acquire RCpc instructions v2)
|
||||||
- FEAT_LSE (Large System Extensions)
|
- FEAT_LSE (Large System Extensions)
|
||||||
|
- FEAT_LVA (Large Virtual Address space)
|
||||||
- FEAT_MTE (Memory Tagging Extension)
|
- FEAT_MTE (Memory Tagging Extension)
|
||||||
- FEAT_MTE2 (Memory Tagging Extension)
|
- FEAT_MTE2 (Memory Tagging Extension)
|
||||||
- FEAT_MTE3 (MTE Asymmetric Fault Handling)
|
- FEAT_MTE3 (MTE Asymmetric Fault Handling)
|
||||||
|
|
|
@ -488,9 +488,14 @@ static void fdt_add_psci_node(void *fdt)
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_fdt_add_subnode(fdt, "/psci");
|
qemu_fdt_add_subnode(fdt, "/psci");
|
||||||
if (armcpu->psci_version == 2) {
|
if (armcpu->psci_version >= QEMU_PSCI_VERSION_0_2) {
|
||||||
const char comp[] = "arm,psci-0.2\0arm,psci";
|
if (armcpu->psci_version < QEMU_PSCI_VERSION_1_0) {
|
||||||
qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
|
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;
|
cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
|
||||||
if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
|
if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
* Application Note AN524:
|
* Application Note AN524:
|
||||||
* https://developer.arm.com/documentation/dai0524/latest/
|
* https://developer.arm.com/documentation/dai0524/latest/
|
||||||
* Application Note AN547:
|
* 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
|
* 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:
|
* (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 },
|
{ "gpio1", make_unimp_dev, &mms->gpio[1], 0x41101000, 0x1000 },
|
||||||
{ "gpio2", make_unimp_dev, &mms->gpio[2], 0x41102000, 0x1000 },
|
{ "gpio2", make_unimp_dev, &mms->gpio[2], 0x41102000, 0x1000 },
|
||||||
{ "gpio3", make_unimp_dev, &mms->gpio[3], 0x41103000, 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 } },
|
{ "eth-usb", make_eth_usb, NULL, 0x41400000, 0x200000, { 49 } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -30,24 +30,6 @@
|
||||||
#define PCA9548_CHANNEL_COUNT 8
|
#define PCA9548_CHANNEL_COUNT 8
|
||||||
#define PCA9546_CHANNEL_COUNT 4
|
#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.
|
* struct Pca954xState - The pca954x state object.
|
||||||
* @control: The value written to the mux control.
|
* @control: The value written to the mux control.
|
||||||
|
@ -59,8 +41,8 @@ typedef struct Pca954xState {
|
||||||
|
|
||||||
uint8_t control;
|
uint8_t control;
|
||||||
|
|
||||||
/* The channel i2c buses. */
|
bool enabled[PCA9548_CHANNEL_COUNT];
|
||||||
Pca954xChannel channel[PCA9548_CHANNEL_COUNT];
|
I2CBus *bus[PCA9548_CHANNEL_COUNT];
|
||||||
} Pca954xState;
|
} Pca954xState;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -98,11 +80,11 @@ static bool pca954x_match(I2CSlave *candidate, uint8_t address,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < mc->nchans; i++) {
|
for (i = 0; i < mc->nchans; i++) {
|
||||||
if (!mux->channel[i].enabled) {
|
if (!mux->enabled[i]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i2c_scan_bus(mux->channel[i].bus, address, broadcast,
|
if (i2c_scan_bus(mux->bus[i], address, broadcast,
|
||||||
current_devs)) {
|
current_devs)) {
|
||||||
if (!broadcast) {
|
if (!broadcast) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -125,9 +107,9 @@ static void pca954x_enable_channel(Pca954xState *s, uint8_t enable_mask)
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < mc->nchans; i++) {
|
for (i = 0; i < mc->nchans; i++) {
|
||||||
if (enable_mask & (1 << i)) {
|
if (enable_mask & (1 << i)) {
|
||||||
s->channel[i].enabled = true;
|
s->enabled[i] = true;
|
||||||
} else {
|
} 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);
|
Pca954xState *pca954x = PCA954X(mux);
|
||||||
|
|
||||||
g_assert(channel < pc->nchans);
|
g_assert(channel < pc->nchans);
|
||||||
return I2C_BUS(qdev_get_child_bus(DEVICE(&pca954x->channel[channel]),
|
return pca954x->bus[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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pca9546_class_init(ObjectClass *klass, void *data)
|
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;
|
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)
|
static void pca954x_init(Object *obj)
|
||||||
{
|
{
|
||||||
Pca954xState *s = PCA954X(obj);
|
Pca954xState *s = PCA954X(obj);
|
||||||
Pca954xClass *c = PCA954X_GET_CLASS(obj);
|
Pca954xClass *c = PCA954X_GET_CLASS(obj);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Only initialize the children we expect. */
|
/* SMBus modules. Cannot fail. */
|
||||||
for (i = 0; i < c->nchans; i++) {
|
for (i = 0; i < c->nchans; i++) {
|
||||||
object_initialize_child(obj, "channel[*]", &s->channel[i],
|
g_autofree gchar *bus_name = g_strdup_printf("i2c.%d", i);
|
||||||
TYPE_PCA954X_CHANNEL);
|
|
||||||
|
/* 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;
|
rc->phases.enter = pca954x_enter_reset;
|
||||||
|
|
||||||
dc->desc = "Pca954x i2c-mux";
|
dc->desc = "Pca954x i2c-mux";
|
||||||
dc->realize = pca954x_realize;
|
|
||||||
|
|
||||||
k->write_data = pca954x_write_data;
|
k->write_data = pca954x_write_data;
|
||||||
k->receive_byte = pca954x_read_byte;
|
k->receive_byte = pca954x_read_byte;
|
||||||
|
@ -278,13 +234,6 @@ static const TypeInfo pca954x_info[] = {
|
||||||
.parent = TYPE_PCA954X,
|
.parent = TYPE_PCA954X,
|
||||||
.class_init = pca9548_class_init,
|
.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)
|
DEFINE_TYPES(pca954x_info)
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
#include "sysemu/reset.h"
|
#include "sysemu/reset.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
#include "hw/arm/omap.h" /* For I2SCodec */
|
#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;
|
TSC210xState *s = opaque;
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
|
|
||||||
if (len != 16)
|
if (len != 16) {
|
||||||
hw_error("%s: FIXME: bad SPI word width %i\n", __func__, len);
|
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
|
/* TODO: sequential reads etc - how do we make sure the host doesn't
|
||||||
* unintentionally read out a conversion result from a register while
|
* unintentionally read out a conversion result from a register while
|
||||||
|
|
|
@ -59,6 +59,19 @@
|
||||||
extract64((storage), R_ ## reg ## _ ## field ## _SHIFT, \
|
extract64((storage), R_ ## reg ## _ ## field ## _SHIFT, \
|
||||||
R_ ## reg ## _ ## field ## _LENGTH)
|
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 */
|
/* Extract a field from an array of registers */
|
||||||
#define ARRAY_FIELD_EX32(regs, reg, field) \
|
#define ARRAY_FIELD_EX32(regs, reg, field) \
|
||||||
FIELD_EX32((regs)[R_ ## reg], reg, field)
|
FIELD_EX32((regs)[R_ ## reg], reg, field)
|
||||||
|
@ -95,7 +108,40 @@
|
||||||
_d; })
|
_d; })
|
||||||
#define FIELD_DP64(storage, reg, field, val) ({ \
|
#define FIELD_DP64(storage, reg, field, val) ({ \
|
||||||
struct { \
|
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 }; \
|
} _v = { .v = val }; \
|
||||||
uint64_t _d; \
|
uint64_t _d; \
|
||||||
_d = deposit64((storage), R_ ## reg ## _ ## field ## _SHIFT, \
|
_d = deposit64((storage), R_ ## reg ## _ ## field ## _SHIFT, \
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
|
|
||||||
#ifdef TARGET_AARCH64
|
#ifdef TARGET_AARCH64
|
||||||
# define TARGET_LONG_BITS 64
|
# define TARGET_LONG_BITS 64
|
||||||
# define TARGET_PHYS_ADDR_SPACE_BITS 48
|
# define TARGET_PHYS_ADDR_SPACE_BITS 52
|
||||||
# define TARGET_VIRT_ADDR_SPACE_BITS 48
|
# define TARGET_VIRT_ADDR_SPACE_BITS 52
|
||||||
#else
|
#else
|
||||||
# define TARGET_LONG_BITS 32
|
# define TARGET_LONG_BITS 32
|
||||||
# define TARGET_PHYS_ADDR_SPACE_BITS 40
|
# define TARGET_PHYS_ADDR_SPACE_BITS 40
|
||||||
|
|
|
@ -206,10 +206,11 @@ static void arm_cpu_reset(DeviceState *dev)
|
||||||
aarch64_sve_zcr_get_valid_len(cpu, cpu->sve_default_vq - 1);
|
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.
|
* Enable TBI0 but not TBI1.
|
||||||
* Note that this must match useronly_clean_ptr.
|
* 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 */
|
/* Enable MTE */
|
||||||
if (cpu_isar_feature(aa64_mte, cpu)) {
|
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.
|
* picky DTB consumer will also provide a helpful error message.
|
||||||
*/
|
*/
|
||||||
cpu->dtb_compatible = "qemu,unknown";
|
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;
|
cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
|
||||||
|
|
||||||
if (tcg_enabled() || hvf_enabled()) {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4284,11 +4284,38 @@ static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id)
|
||||||
return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0;
|
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)
|
static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id)
|
||||||
{
|
{
|
||||||
return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0;
|
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)
|
static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id)
|
||||||
{
|
{
|
||||||
return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0;
|
return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0;
|
||||||
|
|
|
@ -795,7 +795,11 @@ static void aarch64_max_initfn(Object *obj)
|
||||||
cpu->isar.id_aa64pfr1 = t;
|
cpu->isar.id_aa64pfr1 = t;
|
||||||
|
|
||||||
t = cpu->isar.id_aa64mmfr0;
|
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;
|
cpu->isar.id_aa64mmfr0 = t;
|
||||||
|
|
||||||
t = cpu->isar.id_aa64mmfr1;
|
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, UAO, 1);
|
||||||
t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* TTCNP */
|
t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* TTCNP */
|
||||||
t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* TTST */
|
t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* TTST */
|
||||||
|
t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */
|
||||||
cpu->isar.id_aa64mmfr2 = t;
|
cpu->isar.id_aa64mmfr2 = t;
|
||||||
|
|
||||||
t = cpu->isar.id_aa64zfr0;
|
t = cpu->isar.id_aa64zfr0;
|
||||||
|
|
|
@ -4511,70 +4511,73 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TARGET_AARCH64
|
#ifdef TARGET_AARCH64
|
||||||
static uint64_t tlbi_aa64_range_get_length(CPUARMState *env,
|
typedef struct {
|
||||||
uint64_t value)
|
uint64_t base;
|
||||||
{
|
|
||||||
unsigned int page_shift;
|
|
||||||
unsigned int page_size_granule;
|
|
||||||
uint64_t num;
|
|
||||||
uint64_t scale;
|
|
||||||
uint64_t exponent;
|
|
||||||
uint64_t length;
|
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);
|
page_size_granule = extract64(value, 46, 2);
|
||||||
|
|
||||||
if (page_size_granule == 0) {
|
/* The granule encoded in value must match the granule in use. */
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "Invalid page size granule %d\n",
|
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);
|
page_size_granule);
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
page_shift = (page_size_granule - 1) * 2 + 12;
|
page_shift = (page_size_granule - 1) * 2 + 12;
|
||||||
|
num = extract64(value, 39, 5);
|
||||||
|
scale = extract64(value, 44, 2);
|
||||||
exponent = (5 * scale) + 1;
|
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,
|
if (param.select) {
|
||||||
bool two_ranges)
|
ret.base = sextract64(value, 0, 37);
|
||||||
{
|
|
||||||
/* TODO: ARMv8.7 FEAT_LPA2 */
|
|
||||||
uint64_t pageaddr;
|
|
||||||
|
|
||||||
if (two_ranges) {
|
|
||||||
pageaddr = sextract64(value, 0, 37) << TARGET_PAGE_BITS;
|
|
||||||
} else {
|
} 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,
|
static void do_rvae_write(CPUARMState *env, uint64_t value,
|
||||||
int idxmap, bool synced)
|
int idxmap, bool synced)
|
||||||
{
|
{
|
||||||
ARMMMUIdx one_idx = ARM_MMU_IDX_A | ctz32(idxmap);
|
ARMMMUIdx one_idx = ARM_MMU_IDX_A | ctz32(idxmap);
|
||||||
bool two_ranges = regime_has_2_ranges(one_idx);
|
TLBIRange range;
|
||||||
uint64_t baseaddr, length;
|
|
||||||
int bits;
|
int bits;
|
||||||
|
|
||||||
baseaddr = tlbi_aa64_range_get_base(env, value, two_ranges);
|
range = tlbi_aa64_get_range(env, one_idx, value);
|
||||||
length = tlbi_aa64_range_get_length(env, value);
|
bits = tlbbits_for_regime(env, one_idx, range.base);
|
||||||
bits = tlbbits_for_regime(env, one_idx, baseaddr);
|
|
||||||
|
|
||||||
if (synced) {
|
if (synced) {
|
||||||
tlb_flush_range_by_mmuidx_all_cpus_synced(env_cpu(env),
|
tlb_flush_range_by_mmuidx_all_cpus_synced(env_cpu(env),
|
||||||
baseaddr,
|
range.base,
|
||||||
length,
|
range.length,
|
||||||
idxmap,
|
idxmap,
|
||||||
bits);
|
bits);
|
||||||
} else {
|
} else {
|
||||||
tlb_flush_range_by_mmuidx(env_cpu(env), baseaddr,
|
tlb_flush_range_by_mmuidx(env_cpu(env), range.base,
|
||||||
length, idxmap, bits);
|
range.length, idxmap, bits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6423,11 +6426,18 @@ static void dbgwvr_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
ARMCPU *cpu = env_archcpu(env);
|
ARMCPU *cpu = env_archcpu(env);
|
||||||
int i = ri->crm;
|
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.
|
* 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);
|
raw_write(env, ri, value);
|
||||||
hw_watchpoint_update(cpu, i);
|
hw_watchpoint_update(cpu, i);
|
||||||
|
@ -6473,10 +6483,19 @@ void hw_breakpoint_update(ARMCPU *cpu, int n)
|
||||||
case 0: /* unlinked address match */
|
case 0: /* unlinked address match */
|
||||||
case 1: /* linked 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
|
* Bits [1:0] are RES0.
|
||||||
* RES0. The BAS field is used to allow setting breakpoints on 16
|
*
|
||||||
* bit wide instructions; it is CONSTRAINED UNPREDICTABLE whether
|
* 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
|
* 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
|
* 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
|
* 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).
|
* See also figure D2-3 in the v8 ARM ARM (DDI0487A.c).
|
||||||
*/
|
*/
|
||||||
int bas = extract64(bcr, 5, 4);
|
int bas = extract64(bcr, 5, 4);
|
||||||
addr = sextract64(bvr, 0, 49) & ~3ULL;
|
addr = bvr & ~3ULL;
|
||||||
if (bas == 0) {
|
if (bas == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -11065,13 +11084,18 @@ do_fault:
|
||||||
* false otherwise.
|
* false otherwise.
|
||||||
*/
|
*/
|
||||||
static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level,
|
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;
|
const int grainsize = stride + 3;
|
||||||
int startsizecheck;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11081,22 +11105,19 @@ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_aa64) {
|
if (is_aa64) {
|
||||||
CPUARMState *env = &cpu->env;
|
|
||||||
unsigned int pamax = arm_pamax(cpu);
|
|
||||||
|
|
||||||
switch (stride) {
|
switch (stride) {
|
||||||
case 13: /* 64KB Pages. */
|
case 13: /* 64KB Pages. */
|
||||||
if (level == 0 || (level == 1 && pamax <= 42)) {
|
if (level == 0 || (level == 1 && outputsize <= 42)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 11: /* 16KB Pages. */
|
case 11: /* 16KB Pages. */
|
||||||
if (level == 0 || (level == 1 && pamax <= 40)) {
|
if (level == 0 || (level == 1 && outputsize <= 40)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 9: /* 4KB Pages. */
|
case 9: /* 4KB Pages. */
|
||||||
if (level == 0 && pamax <= 42) {
|
if (level == 0 && outputsize <= 42) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -11105,8 +11126,8 @@ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inputsize checks. */
|
/* Inputsize checks. */
|
||||||
if (inputsize > pamax &&
|
if (inputsize > outputsize &&
|
||||||
(arm_el_is_aa64(env, 1) || inputsize > 40)) {
|
(arm_el_is_aa64(&cpu->env, 1) || inputsize > 40)) {
|
||||||
/* This is CONSTRAINED UNPREDICTABLE and we choose to fault. */
|
/* This is CONSTRAINED UNPREDICTABLE and we choose to fault. */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -11152,6 +11173,31 @@ static uint8_t convert_stage2_attrs(CPUARMState *env, uint8_t s2attrs)
|
||||||
}
|
}
|
||||||
#endif /* !CONFIG_USER_ONLY */
|
#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)
|
static int aa64_va_parameter_tbi(uint64_t tcr, ARMMMUIdx mmu_idx)
|
||||||
{
|
{
|
||||||
if (regime_has_2_ranges(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)
|
ARMMMUIdx mmu_idx, bool data)
|
||||||
{
|
{
|
||||||
uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr;
|
uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr;
|
||||||
bool epd, hpd, using16k, using64k;
|
bool epd, hpd, using16k, using64k, tsz_oob, ds;
|
||||||
int select, tsz, tbi, max_tsz;
|
int select, tsz, tbi, max_tsz, min_tsz, ps, sh;
|
||||||
|
ARMCPU *cpu = env_archcpu(env);
|
||||||
|
|
||||||
if (!regime_has_2_ranges(mmu_idx)) {
|
if (!regime_has_2_ranges(mmu_idx)) {
|
||||||
select = 0;
|
select = 0;
|
||||||
|
@ -11205,6 +11252,9 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
|
||||||
hpd = extract32(tcr, 24, 1);
|
hpd = extract32(tcr, 24, 1);
|
||||||
}
|
}
|
||||||
epd = false;
|
epd = false;
|
||||||
|
sh = extract32(tcr, 12, 2);
|
||||||
|
ps = extract32(tcr, 16, 3);
|
||||||
|
ds = extract64(tcr, 32, 1);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Bit 55 is always between the two regions, and is canonical for
|
* 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) {
|
if (!select) {
|
||||||
tsz = extract32(tcr, 0, 6);
|
tsz = extract32(tcr, 0, 6);
|
||||||
epd = extract32(tcr, 7, 1);
|
epd = extract32(tcr, 7, 1);
|
||||||
|
sh = extract32(tcr, 12, 2);
|
||||||
using64k = extract32(tcr, 14, 1);
|
using64k = extract32(tcr, 14, 1);
|
||||||
using16k = extract32(tcr, 15, 1);
|
using16k = extract32(tcr, 15, 1);
|
||||||
hpd = extract64(tcr, 41, 1);
|
hpd = extract64(tcr, 41, 1);
|
||||||
|
@ -11223,18 +11274,61 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
|
||||||
using64k = tg == 3;
|
using64k = tg == 3;
|
||||||
tsz = extract32(tcr, 16, 6);
|
tsz = extract32(tcr, 16, 6);
|
||||||
epd = extract32(tcr, 23, 1);
|
epd = extract32(tcr, 23, 1);
|
||||||
|
sh = extract32(tcr, 28, 2);
|
||||||
hpd = extract64(tcr, 42, 1);
|
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;
|
max_tsz = 48 - using64k;
|
||||||
} else {
|
} else {
|
||||||
max_tsz = 39;
|
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. */
|
/* Present TBI as a composite with TBID. */
|
||||||
tbi = aa64_va_parameter_tbi(tcr, mmu_idx);
|
tbi = aa64_va_parameter_tbi(tcr, mmu_idx);
|
||||||
|
@ -11245,12 +11339,16 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
|
||||||
|
|
||||||
return (ARMVAParameters) {
|
return (ARMVAParameters) {
|
||||||
.tsz = tsz,
|
.tsz = tsz,
|
||||||
|
.ps = ps,
|
||||||
|
.sh = sh,
|
||||||
.select = select,
|
.select = select,
|
||||||
.tbi = tbi,
|
.tbi = tbi,
|
||||||
.epd = epd,
|
.epd = epd,
|
||||||
.hpd = hpd,
|
.hpd = hpd,
|
||||||
.using16k = using16k,
|
.using16k = using16k,
|
||||||
.using64k = using64k,
|
.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;
|
target_ulong page_size;
|
||||||
uint32_t attrs;
|
uint32_t attrs;
|
||||||
int32_t stride;
|
int32_t stride;
|
||||||
int addrsize, inputsize;
|
int addrsize, inputsize, outputsize;
|
||||||
TCR *tcr = regime_tcr(env, mmu_idx);
|
TCR *tcr = regime_tcr(env, mmu_idx);
|
||||||
int ap, ns, xn, pxn;
|
int ap, ns, xn, pxn;
|
||||||
uint32_t el = regime_el(env, mmu_idx);
|
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. */
|
/* TODO: This code does not support shareability levels. */
|
||||||
if (aarch64) {
|
if (aarch64) {
|
||||||
|
int ps;
|
||||||
|
|
||||||
param = aa64_va_parameters(env, address, mmu_idx,
|
param = aa64_va_parameters(env, address, mmu_idx,
|
||||||
access_type != MMU_INST_FETCH);
|
access_type != MMU_INST_FETCH);
|
||||||
level = 0;
|
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;
|
addrsize = 64 - 8 * param.tbi;
|
||||||
inputsize = 64 - param.tsz;
|
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 {
|
} else {
|
||||||
param = aa32_va_parameters(env, address, mmu_idx);
|
param = aa32_va_parameters(env, address, mmu_idx);
|
||||||
level = 1;
|
level = 1;
|
||||||
addrsize = (mmu_idx == ARMMMUIdx_Stage2 ? 40 : 32);
|
addrsize = (mmu_idx == ARMMMUIdx_Stage2 ? 40 : 32);
|
||||||
inputsize = addrsize - param.tsz;
|
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)
|
* VTCR_EL2.SL0 field (whose interpretation depends on the page size)
|
||||||
*/
|
*/
|
||||||
uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2);
|
uint32_t sl0 = extract32(tcr->raw_tcr, 6, 2);
|
||||||
|
uint32_t sl2 = extract64(tcr->raw_tcr, 33, 1);
|
||||||
uint32_t startlevel;
|
uint32_t startlevel;
|
||||||
bool ok;
|
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 */
|
/* AArch32 or 4KB pages */
|
||||||
startlevel = 2 - sl0;
|
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. */
|
/* Check that the starting level is valid. */
|
||||||
ok = check_s2_mmu_setup(cpu, aarch64, startlevel,
|
ok = check_s2_mmu_setup(cpu, aarch64, startlevel,
|
||||||
inputsize, stride);
|
inputsize, stride, outputsize);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
fault_type = ARMFault_Translation;
|
fault_type = ARMFault_Translation;
|
||||||
goto do_fault;
|
goto do_fault;
|
||||||
|
@ -11473,24 +11608,49 @@ static bool get_phys_addr_lpae(CPUARMState *env, uint64_t address,
|
||||||
level = startlevel;
|
level = startlevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
indexmask_grainsize = (1ULL << (stride + 3)) - 1;
|
indexmask_grainsize = MAKE_64BIT_MASK(0, stride + 3);
|
||||||
indexmask = (1ULL << (inputsize - (stride * (4 - level)))) - 1;
|
indexmask = MAKE_64BIT_MASK(0, inputsize - (stride * (4 - level)));
|
||||||
|
|
||||||
/* Now we can extract the actual base address from the TTBR */
|
/* Now we can extract the actual base address from the TTBR */
|
||||||
descaddr = extract64(ttbr, 0, 48);
|
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
|
* 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.
|
* and also to mask out CnP (bit 0) which could validly be non-zero.
|
||||||
*/
|
*/
|
||||||
descaddr &= ~indexmask;
|
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
|
* For AArch32, the address field in the descriptor goes up to bit 39
|
||||||
* up to bit 39 for AArch32, because we don't need other bits in that case
|
* for both v7 and v8. However, for v8 the SBZ bits [47:40] must be 0
|
||||||
* to construct next descriptor address (anyway they should be all zeroes).
|
* 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) &
|
if (param.ds) {
|
||||||
~indexmask_grainsize;
|
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
|
/* Secure accesses start with the page table in secure memory and
|
||||||
* can be downgraded to non-secure at any step. Non-secure accesses
|
* 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 */
|
/* Invalid, or the Reserved level 3 encoding */
|
||||||
goto do_fault;
|
goto do_fault;
|
||||||
}
|
}
|
||||||
|
|
||||||
descaddr = descriptor & descaddrmask;
|
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)) {
|
if ((descriptor & 2) && (level < 3)) {
|
||||||
/* Table entry. The top five bits are attributes which may
|
/* Table entry. The top five bits are attributes which may
|
||||||
* propagate down through lower levels of the table (and
|
* 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);
|
assert(attrindx <= 7);
|
||||||
cacheattrs->attrs = extract64(mair, attrindx * 8, 8);
|
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;
|
*phys_ptr = descaddr;
|
||||||
*page_size_ptr = page_size;
|
*page_size_ptr = page_size;
|
||||||
|
|
|
@ -678,7 +678,7 @@ static bool hvf_handle_psci_call(CPUState *cpu)
|
||||||
|
|
||||||
switch (param[0]) {
|
switch (param[0]) {
|
||||||
case QEMU_PSCI_0_2_FN_PSCI_VERSION:
|
case QEMU_PSCI_0_2_FN_PSCI_VERSION:
|
||||||
ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
|
ret = QEMU_PSCI_VERSION_1_1;
|
||||||
break;
|
break;
|
||||||
case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
|
case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
|
||||||
ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
|
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:
|
case QEMU_PSCI_0_2_FN_MIGRATE:
|
||||||
ret = QEMU_PSCI_RET_NOT_SUPPORTED;
|
ret = QEMU_PSCI_RET_NOT_SUPPORTED;
|
||||||
break;
|
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:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,24 +243,7 @@ static inline void update_spsel(CPUARMState *env, uint32_t imm)
|
||||||
* Returns the implementation defined bit-width of physical addresses.
|
* Returns the implementation defined bit-width of physical addresses.
|
||||||
* The ARMv8 reference manuals refer to this as PAMax().
|
* The ARMv8 reference manuals refer to this as PAMax().
|
||||||
*/
|
*/
|
||||||
static inline unsigned int arm_pamax(ARMCPU *cpu)
|
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return true if extended addresses are enabled.
|
/* Return true if extended addresses are enabled.
|
||||||
* This is always the case if our translation regime is 64 bit,
|
* 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:
|
case ARMFault_None:
|
||||||
return 0;
|
return 0;
|
||||||
case ARMFault_AddressSize:
|
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;
|
break;
|
||||||
case ARMFault_AccessFlag:
|
case ARMFault_AccessFlag:
|
||||||
fsc = (fi->level & 3) | (0x2 << 2);
|
assert(fi->level >= 0 && fi->level <= 3);
|
||||||
|
fsc = 0b001000 | fi->level;
|
||||||
break;
|
break;
|
||||||
case ARMFault_Permission:
|
case ARMFault_Permission:
|
||||||
fsc = (fi->level & 3) | (0x3 << 2);
|
assert(fi->level >= 0 && fi->level <= 3);
|
||||||
|
fsc = 0b001100 | fi->level;
|
||||||
break;
|
break;
|
||||||
case ARMFault_Translation:
|
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;
|
break;
|
||||||
case ARMFault_SyncExternal:
|
case ARMFault_SyncExternal:
|
||||||
fsc = 0x10 | (fi->ea << 12);
|
fsc = 0x10 | (fi->ea << 12);
|
||||||
break;
|
break;
|
||||||
case ARMFault_SyncExternalOnWalk:
|
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;
|
break;
|
||||||
case ARMFault_SyncParity:
|
case ARMFault_SyncParity:
|
||||||
fsc = 0x18;
|
fsc = 0x18;
|
||||||
break;
|
break;
|
||||||
case ARMFault_SyncParityOnWalk:
|
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;
|
break;
|
||||||
case ARMFault_AsyncParity:
|
case ARMFault_AsyncParity:
|
||||||
fsc = 0x19;
|
fsc = 0x19;
|
||||||
|
@ -1049,12 +1055,16 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id)
|
||||||
*/
|
*/
|
||||||
typedef struct ARMVAParameters {
|
typedef struct ARMVAParameters {
|
||||||
unsigned tsz : 8;
|
unsigned tsz : 8;
|
||||||
|
unsigned ps : 3;
|
||||||
|
unsigned sh : 2;
|
||||||
unsigned select : 1;
|
unsigned select : 1;
|
||||||
bool tbi : 1;
|
bool tbi : 1;
|
||||||
bool epd : 1;
|
bool epd : 1;
|
||||||
bool hpd : 1;
|
bool hpd : 1;
|
||||||
bool using16k : 1;
|
bool using16k : 1;
|
||||||
bool using64k : 1;
|
bool using64k : 1;
|
||||||
|
bool tsz_oob : 1; /* tsz has been clamped to legal range */
|
||||||
|
bool ds : 1;
|
||||||
} ARMVAParameters;
|
} ARMVAParameters;
|
||||||
|
|
||||||
ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
|
ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va,
|
||||||
|
|
|
@ -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_AFFINITY_INFO QEMU_PSCI_0_2_FN64(4)
|
||||||
#define QEMU_PSCI_0_2_FN64_MIGRATE QEMU_PSCI_0_2_FN64(5)
|
#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_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_OFF, PSCI_0_2_FN_CPU_OFF);
|
||||||
MISMATCH_CHECK(QEMU_PSCI_0_2_FN_CPU_ON, PSCI_0_2_FN_CPU_ON);
|
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_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_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_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 */
|
/* PSCI v0.2 return values used by TCG emulation of PSCI */
|
||||||
|
|
||||||
/* No Trusted OS migration to worry about when offlining CPUs */
|
/* No Trusted OS migration to worry about when offlining CPUs */
|
||||||
#define QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED 2
|
#define QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED 2
|
||||||
|
|
||||||
/* We implement version 0.2 only */
|
#define QEMU_PSCI_VERSION_0_1 0x00001
|
||||||
#define QEMU_PSCI_0_2_RET_VERSION_0_2 2
|
#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_TOS_MIGRATION_NOT_REQUIRED, PSCI_0_2_TOS_MP);
|
||||||
MISMATCH_CHECK(QEMU_PSCI_0_2_RET_VERSION_0_2,
|
/* We don't bother to check every possible version value */
|
||||||
(PSCI_VERSION_MAJOR(0) | PSCI_VERSION_MINOR(2)));
|
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) */
|
/* PSCI return values (inclusive of all PSCI versions) */
|
||||||
#define QEMU_PSCI_RET_SUCCESS 0
|
#define QEMU_PSCI_RET_SUCCESS 0
|
||||||
|
|
|
@ -849,6 +849,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||||
uint64_t mpidr;
|
uint64_t mpidr;
|
||||||
ARMCPU *cpu = ARM_CPU(cs);
|
ARMCPU *cpu = ARM_CPU(cs);
|
||||||
CPUARMState *env = &cpu->env;
|
CPUARMState *env = &cpu->env;
|
||||||
|
uint64_t psciver;
|
||||||
|
|
||||||
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
|
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
|
||||||
!object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU)) {
|
!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;
|
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_POWER_OFF;
|
||||||
}
|
}
|
||||||
if (kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PSCI_0_2)) {
|
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;
|
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2;
|
||||||
}
|
}
|
||||||
if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
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.
|
* 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
|
* Currently KVM has its own idea about MPIDR assignment, so we
|
||||||
|
|
|
@ -57,7 +57,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This function partially implements the logic for dispatching Power State
|
* 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,
|
* to the extent required for bringing up and taking down secondary cores,
|
||||||
* and for handling reset and poweroff requests.
|
* and for handling reset and poweroff requests.
|
||||||
* Additional information about the calling convention used is available in
|
* 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)) {
|
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;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
|
||||||
ARMCPU *target_cpu;
|
ARMCPU *target_cpu;
|
||||||
|
|
||||||
case QEMU_PSCI_0_2_FN_PSCI_VERSION:
|
case QEMU_PSCI_0_2_FN_PSCI_VERSION:
|
||||||
ret = QEMU_PSCI_0_2_RET_VERSION_0_2;
|
ret = QEMU_PSCI_VERSION_1_1;
|
||||||
break;
|
break;
|
||||||
case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
|
case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE:
|
||||||
ret = QEMU_PSCI_0_2_RET_TOS_MIGRATION_NOT_REQUIRED; /* No trusted OS */
|
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);
|
helper_wfi(env, 4);
|
||||||
break;
|
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_1_FN_MIGRATE:
|
||||||
case QEMU_PSCI_0_2_FN_MIGRATE:
|
case QEMU_PSCI_0_2_FN_MIGRATE:
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -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);
|
tcg_temp_free_i32(tcg_shift);
|
||||||
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
|
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
|
||||||
|
tcg_temp_free_ptr(tcg_fpstatus);
|
||||||
tcg_temp_free_i32(tcg_rmode);
|
tcg_temp_free_i32(tcg_rmode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,6 +190,7 @@ qtests_npcm7xx = \
|
||||||
'npcm7xx_gpio-test',
|
'npcm7xx_gpio-test',
|
||||||
'npcm7xx_pwm-test',
|
'npcm7xx_pwm-test',
|
||||||
'npcm7xx_rng-test',
|
'npcm7xx_rng-test',
|
||||||
|
'npcm7xx_sdhci-test',
|
||||||
'npcm7xx_smbus-test',
|
'npcm7xx_smbus-test',
|
||||||
'npcm7xx_timer-test',
|
'npcm7xx_timer-test',
|
||||||
'npcm7xx_watchdog_timer-test'] + \
|
'npcm7xx_watchdog_timer-test'] + \
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
31
ui/cocoa.m
31
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;
|
NSSize frameSize;
|
||||||
QemuUIInfo info;
|
QemuUIInfo info;
|
||||||
|
|
||||||
|
@ -554,6 +555,25 @@ QemuCocoaView *cocoaView;
|
||||||
dpy_set_ui_info(dcl.con, &info, TRUE);
|
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
|
- (void)viewDidMoveToWindow
|
||||||
{
|
{
|
||||||
[self updateUIInfo];
|
[self updateUIInfo];
|
||||||
|
@ -1956,8 +1976,6 @@ int main (int argc, char **argv) {
|
||||||
static void cocoa_update(DisplayChangeListener *dcl,
|
static void cocoa_update(DisplayChangeListener *dcl,
|
||||||
int x, int y, int w, int h)
|
int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
|
|
||||||
COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
|
COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
@ -1973,20 +1991,15 @@ static void cocoa_update(DisplayChangeListener *dcl,
|
||||||
}
|
}
|
||||||
[cocoaView setNeedsDisplayInRect:rect];
|
[cocoaView setNeedsDisplayInRect:rect];
|
||||||
});
|
});
|
||||||
|
|
||||||
[pool release];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cocoa_switch(DisplayChangeListener *dcl,
|
static void cocoa_switch(DisplayChangeListener *dcl,
|
||||||
DisplaySurface *surface)
|
DisplaySurface *surface)
|
||||||
{
|
{
|
||||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
pixman_image_t *image = surface->image;
|
pixman_image_t *image = surface->image;
|
||||||
|
|
||||||
COCOA_DEBUG("qemu_cocoa: cocoa_switch\n");
|
COCOA_DEBUG("qemu_cocoa: cocoa_switch\n");
|
||||||
|
|
||||||
[cocoaView updateUIInfo];
|
|
||||||
|
|
||||||
// The DisplaySurface will be freed as soon as this callback returns.
|
// The DisplaySurface will be freed as soon as this callback returns.
|
||||||
// We take a reference to the underlying pixman image here so it does
|
// We take a reference to the underlying pixman image here so it does
|
||||||
// not disappear from under our feet; the switchSurface method will
|
// not disappear from under our feet; the switchSurface method will
|
||||||
|
@ -1994,9 +2007,9 @@ static void cocoa_switch(DisplayChangeListener *dcl,
|
||||||
pixman_image_ref(image);
|
pixman_image_ref(image);
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[cocoaView updateUIInfo];
|
||||||
[cocoaView switchSurface:image];
|
[cocoaView switchSurface:image];
|
||||||
});
|
});
|
||||||
[pool release];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cocoa_refresh(DisplayChangeListener *dcl)
|
static void cocoa_refresh(DisplayChangeListener *dcl)
|
||||||
|
|
Loading…
Reference in New Issue