target-arm queue:
* report ARMv8-A FP support for AArch32 -cpu max * hw/ssi/xilinx_spips: Avoid AXI writes to the LQSPI linear memory * hw/ssi/xilinx_spips: Avoid out-of-bound access to lqspi_buf[] * hw/ssi/mss-spi: Avoid crash when reading empty RX FIFO * hw/display/xlnx_dp: Avoid crash when reading empty RX FIFO * hw/arm/virt: Fix non-secure flash mode * pl031: Correctly migrate state when using -rtc clock=host * fix regression that meant arm926 and arm1026 lost VFP double-precision support * v8M: NS BusFault on vector table fetch escalates to NS HardFault -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl0sgoUZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3m8UEAClHrOpyfFdOdlkjh6QTvvE dxsKVVtQ7hy0pH98LA9QrFwLq5e8N+RpJQfPipKgoQ1l/I/l1/1KV41R38mubcdB CcJTOX/BZuC8I9IHOX1oA9UJh9WMrEL/52lhYuzFD03mWPS28nUO8jRsM9pqYJ/3 KRNy20t0G13omq+BVPlj974J2SCHS2cJ67l3FPkxm1c+VR3MWPb2I3U30w9HRxCj 9so+QSw4cJ6lU5uJU/zXcsyFWR0h8p0lNZ6yfKtsm8mxqgKdNSKnNa7yLP+ODEBe GFDkOrTLrZcTU9Dw9IocRYBOZeFoRJdlkVVPeOju7eX8W4CdvKf2MxLaJu7uT+Qj CB3+kBJ1cCMDWW+JSlXuDRl9Uvk/a28AAT8LdWbjjKKcVZvGlONKb7o6vhsPPcjY YKTrbwac+D287f2RKrUv5aVcNr/9bd+bEpkqFBayz+hoJU1xfji+wiSOL1hbzWeD isuFrBphscAKdM7i7+Pn/wiL8jy428ZwVh7+SaR1WBqtOyk8BaC3vBPOlwacun94 US2WYVXuQRFfZPQSTf3ZWatcK4aelZJqlFZkGDtBfHyWRFOuG2HEKtA24BuU9mex U6e69gvNJfAKpaZ3bsNpAzW+1RGtn7ztVw0BkICPiiEMfc/ivGa9T0VJF9IEbMUY 2rAh/QP0g5rwla4hSa3AwQ== =1077 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190715' into staging target-arm queue: * report ARMv8-A FP support for AArch32 -cpu max * hw/ssi/xilinx_spips: Avoid AXI writes to the LQSPI linear memory * hw/ssi/xilinx_spips: Avoid out-of-bound access to lqspi_buf[] * hw/ssi/mss-spi: Avoid crash when reading empty RX FIFO * hw/display/xlnx_dp: Avoid crash when reading empty RX FIFO * hw/arm/virt: Fix non-secure flash mode * pl031: Correctly migrate state when using -rtc clock=host * fix regression that meant arm926 and arm1026 lost VFP double-precision support * v8M: NS BusFault on vector table fetch escalates to NS HardFault # gpg: Signature made Mon 15 Jul 2019 14:41:25 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20190715: target/arm: NS BusFault on vector table fetch escalates to NS HardFault target/arm: Set VFP-related MVFR0 fields for arm926 and arm1026 pl031: Correctly migrate state when using -rtc clock=host hw/arm/virt: Fix non-secure flash mode hw/display/xlnx_dp: Avoid crash when reading empty RX FIFO hw/ssi/mss-spi: Avoid crash when reading empty RX FIFO hw/ssi/xilinx_spips: Avoid out-of-bound access to lqspi_buf[] hw/ssi/xilinx_spips: Avoid AXI writes to the LQSPI linear memory hw/ssi/xilinx_spips: Convert lqspi_read() to read_with_attrs target/arm: report ARMv8-A FP support for AArch32 -cpu max Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
0dc6284710
@ -1674,7 +1674,7 @@ static void machvirt_init(MachineState *machine)
|
|||||||
&machine->device_memory->mr);
|
&machine->device_memory->mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
virt_flash_fdt(vms, sysmem, secure_sysmem);
|
virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem);
|
||||||
|
|
||||||
create_gic(vms, pic);
|
create_gic(vms, pic);
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ GlobalProperty hw_compat_4_0[] = {
|
|||||||
{ "virtio-gpu-pci", "edid", "false" },
|
{ "virtio-gpu-pci", "edid", "false" },
|
||||||
{ "virtio-device", "use-started", "false" },
|
{ "virtio-device", "use-started", "false" },
|
||||||
{ "virtio-balloon-device", "qemu-4-0-config-size", "true" },
|
{ "virtio-balloon-device", "qemu-4-0-config-size", "true" },
|
||||||
|
{ "pl031", "migrate-tick-offset", "false" },
|
||||||
};
|
};
|
||||||
const size_t hw_compat_4_0_len = G_N_ELEMENTS(hw_compat_4_0);
|
const size_t hw_compat_4_0_len = G_N_ELEMENTS(hw_compat_4_0);
|
||||||
|
|
||||||
|
@ -427,11 +427,18 @@ static uint8_t xlnx_dp_aux_pop_rx_fifo(XlnxDPState *s)
|
|||||||
uint8_t ret;
|
uint8_t ret;
|
||||||
|
|
||||||
if (fifo8_is_empty(&s->rx_fifo)) {
|
if (fifo8_is_empty(&s->rx_fifo)) {
|
||||||
DPRINTF("rx_fifo underflow..\n");
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
abort();
|
"%s: Reading empty RX_FIFO\n",
|
||||||
}
|
__func__);
|
||||||
|
/*
|
||||||
|
* The datasheet is not clear about the reset value, it seems
|
||||||
|
* to be unspecified. We choose to return '0'.
|
||||||
|
*/
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
ret = fifo8_pop(&s->rx_fifo);
|
ret = fifo8_pop(&s->rx_fifo);
|
||||||
DPRINTF("pop 0x%" PRIX8 " from rx_fifo.\n", ret);
|
DPRINTF("pop 0x%" PRIX8 " from rx_fifo.\n", ret);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,13 @@ spi_read(void *opaque, hwaddr addr, unsigned int size)
|
|||||||
case R_SPI_RX:
|
case R_SPI_RX:
|
||||||
s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL;
|
s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL;
|
||||||
s->regs[R_SPI_STATUS] &= ~S_RXCHOVRF;
|
s->regs[R_SPI_STATUS] &= ~S_RXCHOVRF;
|
||||||
|
if (fifo32_is_empty(&s->rx_fifo)) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"%s: Reading empty RX_FIFO\n",
|
||||||
|
__func__);
|
||||||
|
} else {
|
||||||
ret = fifo32_pop(&s->rx_fifo);
|
ret = fifo32_pop(&s->rx_fifo);
|
||||||
|
}
|
||||||
if (fifo32_is_empty(&s->rx_fifo)) {
|
if (fifo32_is_empty(&s->rx_fifo)) {
|
||||||
s->regs[R_SPI_STATUS] |= S_RXFIFOEMP;
|
s->regs[R_SPI_STATUS] |= S_RXFIFOEMP;
|
||||||
}
|
}
|
||||||
|
@ -1202,28 +1202,47 @@ static void lqspi_load_cache(void *opaque, hwaddr addr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t
|
static MemTxResult lqspi_read(void *opaque, hwaddr addr, uint64_t *value,
|
||||||
lqspi_read(void *opaque, hwaddr addr, unsigned int size)
|
unsigned size, MemTxAttrs attrs)
|
||||||
{
|
{
|
||||||
XilinxQSPIPS *q = opaque;
|
XilinxQSPIPS *q = XILINX_QSPIPS(opaque);
|
||||||
uint32_t ret;
|
|
||||||
|
|
||||||
if (addr >= q->lqspi_cached_addr &&
|
if (addr >= q->lqspi_cached_addr &&
|
||||||
addr <= q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) {
|
addr <= q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) {
|
||||||
uint8_t *retp = &q->lqspi_buf[addr - q->lqspi_cached_addr];
|
uint8_t *retp = &q->lqspi_buf[addr - q->lqspi_cached_addr];
|
||||||
ret = cpu_to_le32(*(uint32_t *)retp);
|
*value = cpu_to_le32(*(uint32_t *)retp);
|
||||||
DB_PRINT_L(1, "addr: %08x, data: %08x\n", (unsigned)addr,
|
DB_PRINT_L(1, "addr: %08" HWADDR_PRIx ", data: %08" PRIx64 "\n",
|
||||||
(unsigned)ret);
|
addr, *value);
|
||||||
return ret;
|
return MEMTX_OK;
|
||||||
} else {
|
|
||||||
lqspi_load_cache(opaque, addr);
|
|
||||||
return lqspi_read(opaque, addr, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lqspi_load_cache(opaque, addr);
|
||||||
|
return lqspi_read(opaque, addr, value, size, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemTxResult lqspi_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
|
unsigned size, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* From UG1085, Chapter 24 (Quad-SPI controllers):
|
||||||
|
* - Writes are ignored
|
||||||
|
* - AXI writes generate an external AXI slave error (SLVERR)
|
||||||
|
*/
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s Unexpected %u-bit access to 0x%" PRIx64
|
||||||
|
" (value: 0x%" PRIx64 "\n",
|
||||||
|
__func__, size << 3, offset, value);
|
||||||
|
|
||||||
|
return MEMTX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionOps lqspi_ops = {
|
static const MemoryRegionOps lqspi_ops = {
|
||||||
.read = lqspi_read,
|
.read_with_attrs = lqspi_read,
|
||||||
|
.write_with_attrs = lqspi_write,
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
|
.impl = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
},
|
||||||
.valid = {
|
.valid = {
|
||||||
.min_access_size = 1,
|
.min_access_size = 1,
|
||||||
.max_access_size = 4
|
.max_access_size = 4
|
||||||
|
@ -199,29 +199,94 @@ static int pl031_pre_save(void *opaque)
|
|||||||
{
|
{
|
||||||
PL031State *s = opaque;
|
PL031State *s = opaque;
|
||||||
|
|
||||||
/* tick_offset is base_time - rtc_clock base time. Instead, we want to
|
/*
|
||||||
* store the base time relative to the QEMU_CLOCK_VIRTUAL for backwards-compatibility. */
|
* The PL031 device model code uses the tick_offset field, which is
|
||||||
|
* the offset between what the guest RTC should read and what the
|
||||||
|
* QEMU rtc_clock reads:
|
||||||
|
* guest_rtc = rtc_clock + tick_offset
|
||||||
|
* and so
|
||||||
|
* tick_offset = guest_rtc - rtc_clock
|
||||||
|
*
|
||||||
|
* We want to migrate this offset, which sounds straightforward.
|
||||||
|
* Unfortunately older versions of QEMU migrated a conversion of this
|
||||||
|
* offset into an offset from the vm_clock. (This was in turn an
|
||||||
|
* attempt to be compatible with even older QEMU versions, but it
|
||||||
|
* has incorrect behaviour if the rtc_clock is not the same as the
|
||||||
|
* vm_clock.) So we put the actual tick_offset into a migration
|
||||||
|
* subsection, and the backwards-compatible time-relative-to-vm_clock
|
||||||
|
* in the main migration state.
|
||||||
|
*
|
||||||
|
* Calculate base time relative to QEMU_CLOCK_VIRTUAL:
|
||||||
|
*/
|
||||||
int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
s->tick_offset_vmstate = s->tick_offset + delta / NANOSECONDS_PER_SECOND;
|
s->tick_offset_vmstate = s->tick_offset + delta / NANOSECONDS_PER_SECOND;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pl031_pre_load(void *opaque)
|
||||||
|
{
|
||||||
|
PL031State *s = opaque;
|
||||||
|
|
||||||
|
s->tick_offset_migrated = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int pl031_post_load(void *opaque, int version_id)
|
static int pl031_post_load(void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
PL031State *s = opaque;
|
PL031State *s = opaque;
|
||||||
|
|
||||||
int64_t delta = qemu_clock_get_ns(rtc_clock) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
/*
|
||||||
s->tick_offset = s->tick_offset_vmstate - delta / NANOSECONDS_PER_SECOND;
|
* If we got the tick_offset subsection, then we can just use
|
||||||
|
* the value in that. Otherwise the source is an older QEMU and
|
||||||
|
* has given us the offset from the vm_clock; convert it back to
|
||||||
|
* an offset from the rtc_clock. This will cause time to incorrectly
|
||||||
|
* go backwards compared to the host RTC, but this is unavoidable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!s->tick_offset_migrated) {
|
||||||
|
int64_t delta = qemu_clock_get_ns(rtc_clock) -
|
||||||
|
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||||
|
s->tick_offset = s->tick_offset_vmstate -
|
||||||
|
delta / NANOSECONDS_PER_SECOND;
|
||||||
|
}
|
||||||
pl031_set_alarm(s);
|
pl031_set_alarm(s);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pl031_tick_offset_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
PL031State *s = opaque;
|
||||||
|
|
||||||
|
s->tick_offset_migrated = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pl031_tick_offset_needed(void *opaque)
|
||||||
|
{
|
||||||
|
PL031State *s = opaque;
|
||||||
|
|
||||||
|
return s->migrate_tick_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_pl031_tick_offset = {
|
||||||
|
.name = "pl031/tick-offset",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.needed = pl031_tick_offset_needed,
|
||||||
|
.post_load = pl031_tick_offset_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(tick_offset, PL031State),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static const VMStateDescription vmstate_pl031 = {
|
static const VMStateDescription vmstate_pl031 = {
|
||||||
.name = "pl031",
|
.name = "pl031",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.pre_save = pl031_pre_save,
|
.pre_save = pl031_pre_save,
|
||||||
|
.pre_load = pl031_pre_load,
|
||||||
.post_load = pl031_post_load,
|
.post_load = pl031_post_load,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32(tick_offset_vmstate, PL031State),
|
VMSTATE_UINT32(tick_offset_vmstate, PL031State),
|
||||||
@ -231,14 +296,33 @@ static const VMStateDescription vmstate_pl031 = {
|
|||||||
VMSTATE_UINT32(im, PL031State),
|
VMSTATE_UINT32(im, PL031State),
|
||||||
VMSTATE_UINT32(is, PL031State),
|
VMSTATE_UINT32(is, PL031State),
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
.subsections = (const VMStateDescription*[]) {
|
||||||
|
&vmstate_pl031_tick_offset,
|
||||||
|
NULL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Property pl031_properties[] = {
|
||||||
|
/*
|
||||||
|
* True to correctly migrate the tick offset of the RTC. False to
|
||||||
|
* obtain backward migration compatibility with older QEMU versions,
|
||||||
|
* at the expense of the guest RTC going backwards compared with the
|
||||||
|
* host RTC when the VM is saved/restored if using -rtc host.
|
||||||
|
* (Even if set to 'true' older QEMU can migrate forward to newer QEMU;
|
||||||
|
* 'false' also permits newer QEMU to migrate to older QEMU.)
|
||||||
|
*/
|
||||||
|
DEFINE_PROP_BOOL("migrate-tick-offset",
|
||||||
|
PL031State, migrate_tick_offset, true),
|
||||||
|
DEFINE_PROP_END_OF_LIST()
|
||||||
|
};
|
||||||
|
|
||||||
static void pl031_class_init(ObjectClass *klass, void *data)
|
static void pl031_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
dc->vmsd = &vmstate_pl031;
|
dc->vmsd = &vmstate_pl031;
|
||||||
|
dc->props = pl031_properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo pl031_info = {
|
static const TypeInfo pl031_info = {
|
||||||
|
@ -33,6 +33,8 @@ typedef struct PL031State {
|
|||||||
*/
|
*/
|
||||||
uint32_t tick_offset_vmstate;
|
uint32_t tick_offset_vmstate;
|
||||||
uint32_t tick_offset;
|
uint32_t tick_offset;
|
||||||
|
bool tick_offset_migrated;
|
||||||
|
bool migrate_tick_offset;
|
||||||
|
|
||||||
uint32_t mr;
|
uint32_t mr;
|
||||||
uint32_t lr;
|
uint32_t lr;
|
||||||
|
@ -1666,6 +1666,12 @@ static void arm926_initfn(Object *obj)
|
|||||||
* set the field to indicate Jazelle support within QEMU.
|
* set the field to indicate Jazelle support within QEMU.
|
||||||
*/
|
*/
|
||||||
cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1);
|
cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1);
|
||||||
|
/*
|
||||||
|
* Similarly, we need to set MVFR0 fields to enable double precision
|
||||||
|
* and short vector support even though ARMv5 doesn't have this register.
|
||||||
|
*/
|
||||||
|
cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1);
|
||||||
|
cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void arm946_initfn(Object *obj)
|
static void arm946_initfn(Object *obj)
|
||||||
@ -1702,6 +1708,12 @@ static void arm1026_initfn(Object *obj)
|
|||||||
* set the field to indicate Jazelle support within QEMU.
|
* set the field to indicate Jazelle support within QEMU.
|
||||||
*/
|
*/
|
||||||
cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1);
|
cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1);
|
||||||
|
/*
|
||||||
|
* Similarly, we need to set MVFR0 fields to enable double precision
|
||||||
|
* and short vector support even though ARMv5 doesn't have this register.
|
||||||
|
*/
|
||||||
|
cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1);
|
||||||
|
cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1);
|
||||||
|
|
||||||
{
|
{
|
||||||
/* The 1026 had an IFAR at c6,c0,0,1 rather than the ARMv6 c6,c0,0,2 */
|
/* The 1026 had an IFAR at c6,c0,0,1 rather than the ARMv6 c6,c0,0,2 */
|
||||||
@ -2452,6 +2464,10 @@ static void arm_max_initfn(Object *obj)
|
|||||||
t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
|
t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1);
|
||||||
cpu->isar.id_isar6 = t;
|
cpu->isar.id_isar6 = t;
|
||||||
|
|
||||||
|
t = cpu->isar.mvfr1;
|
||||||
|
t = FIELD_DP32(t, MVFR1, FPHP, 2); /* v8.0 FP support */
|
||||||
|
cpu->isar.mvfr1 = t;
|
||||||
|
|
||||||
t = cpu->isar.mvfr2;
|
t = cpu->isar.mvfr2;
|
||||||
t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */
|
t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */
|
||||||
t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */
|
t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */
|
||||||
|
@ -624,7 +624,11 @@ static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure,
|
|||||||
if (sattrs.ns) {
|
if (sattrs.ns) {
|
||||||
attrs.secure = false;
|
attrs.secure = false;
|
||||||
} else if (!targets_secure) {
|
} else if (!targets_secure) {
|
||||||
/* NS access to S memory */
|
/*
|
||||||
|
* NS access to S memory: the underlying exception which we escalate
|
||||||
|
* to HardFault is SecureFault, which always targets Secure.
|
||||||
|
*/
|
||||||
|
exc_secure = true;
|
||||||
goto load_fail;
|
goto load_fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -632,6 +636,11 @@ static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure,
|
|||||||
vector_entry = address_space_ldl(arm_addressspace(cs, attrs), addr,
|
vector_entry = address_space_ldl(arm_addressspace(cs, attrs), addr,
|
||||||
attrs, &result);
|
attrs, &result);
|
||||||
if (result != MEMTX_OK) {
|
if (result != MEMTX_OK) {
|
||||||
|
/*
|
||||||
|
* Underlying exception is BusFault: its target security state
|
||||||
|
* depends on BFHFNMINS.
|
||||||
|
*/
|
||||||
|
exc_secure = !(cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK);
|
||||||
goto load_fail;
|
goto load_fail;
|
||||||
}
|
}
|
||||||
*pvec = vector_entry;
|
*pvec = vector_entry;
|
||||||
@ -641,13 +650,17 @@ load_fail:
|
|||||||
/*
|
/*
|
||||||
* All vector table fetch fails are reported as HardFault, with
|
* All vector table fetch fails are reported as HardFault, with
|
||||||
* HFSR.VECTTBL and .FORCED set. (FORCED is set because
|
* HFSR.VECTTBL and .FORCED set. (FORCED is set because
|
||||||
* technically the underlying exception is a MemManage or BusFault
|
* technically the underlying exception is a SecureFault or BusFault
|
||||||
* that is escalated to HardFault.) This is a terminal exception,
|
* that is escalated to HardFault.) This is a terminal exception,
|
||||||
* so we will either take the HardFault immediately or else enter
|
* so we will either take the HardFault immediately or else enter
|
||||||
* lockup (the latter case is handled in armv7m_nvic_set_pending_derived()).
|
* lockup (the latter case is handled in armv7m_nvic_set_pending_derived()).
|
||||||
|
* The HardFault is Secure if BFHFNMINS is 0 (meaning that all HFs are
|
||||||
|
* secure); otherwise it targets the same security state as the
|
||||||
|
* underlying exception.
|
||||||
*/
|
*/
|
||||||
exc_secure = targets_secure ||
|
if (!(cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) {
|
||||||
!(cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK);
|
exc_secure = true;
|
||||||
|
}
|
||||||
env->v7m.hfsr |= R_V7M_HFSR_VECTTBL_MASK | R_V7M_HFSR_FORCED_MASK;
|
env->v7m.hfsr |= R_V7M_HFSR_VECTTBL_MASK | R_V7M_HFSR_FORCED_MASK;
|
||||||
armv7m_nvic_set_pending_derived(env->nvic, ARMV7M_EXCP_HARD, exc_secure);
|
armv7m_nvic_set_pending_derived(env->nvic, ARMV7M_EXCP_HARD, exc_secure);
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user