diff --git a/qemu/exec.c b/qemu/exec.c index 97fef76e..020b7777 100644 --- a/qemu/exec.c +++ b/qemu/exec.c @@ -1315,6 +1315,8 @@ static bool subpage_accepts(void *opaque, hwaddr addr, static const MemoryRegionOps subpage_ops = { subpage_read, subpage_write, + NULL, + NULL, DEVICE_NATIVE_ENDIAN, { 0, 0, false, subpage_accepts, @@ -1377,6 +1379,8 @@ static bool notdirty_mem_accepts(void *opaque, hwaddr addr, static const MemoryRegionOps notdirty_mem_ops = { NULL, notdirty_mem_write, + NULL, + NULL, DEVICE_NATIVE_ENDIAN, { 0, 0, false, notdirty_mem_accepts, diff --git a/qemu/include/exec/memattrs.h b/qemu/include/exec/memattrs.h new file mode 100644 index 00000000..1cb3fc08 --- /dev/null +++ b/qemu/include/exec/memattrs.h @@ -0,0 +1,41 @@ +/* + * Memory transaction attributes + * + * Copyright (c) 2015 Linaro Limited. + * + * Authors: + * Peter Maydell + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef MEMATTRS_H +#define MEMATTRS_H + +/* Every memory transaction has associated with it a set of + * attributes. Some of these are generic (such as the ID of + * the bus master); some are specific to a particular kind of + * bus (such as the ARM Secure/NonSecure bit). We define them + * all as non-overlapping bitfields in a single struct to avoid + * confusion if different parts of QEMU used the same bit for + * different semantics. + */ +typedef struct MemTxAttrs { + /* Bus masters which don't specify any attributes will get this + * (via the MEMTXATTRS_UNSPECIFIED constant), so that we can + * distinguish "all attributes deliberately clear" from + * "didn't specify" if necessary. + */ + unsigned int unspecified:1; +} MemTxAttrs; + +/* Bus masters which don't specify any attributes will get this, + * which has all attribute bits clear except the topmost one + * (so that we can distinguish "all attributes deliberately clear" + * from "didn't specify" if necessary). + */ +#define MEMTXATTRS_UNSPECIFIED ((MemTxAttrs) { .unspecified = 1 }) + +#endif diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index ef28b9d0..b57eacd9 100644 --- a/qemu/include/exec/memory.h +++ b/qemu/include/exec/memory.h @@ -23,6 +23,7 @@ #include "qemu-common.h" #include "exec/cpu-common.h" #include "exec/hwaddr.h" +#include "exec/memattrs.h" #include "qemu/queue.h" #include "qemu/int128.h" #include "qapi/error.h" @@ -61,6 +62,16 @@ struct IOMMUTLBEntry { IOMMUAccessFlags perm; }; +/* New-style MMIO accessors can indicate that the transaction failed. + * A zero (MEMTX_OK) response means success; anything else is a failure + * of some kind. The memory subsystem will bitwise-OR together results + * if it is synthesizing an operation from multiple smaller accesses. + */ +#define MEMTX_OK 0 +#define MEMTX_ERROR (1U << 0) /* device returned an error */ +#define MEMTX_DECODE_ERROR (1U << 1) /* nothing at that address */ +typedef uint32_t MemTxResult; + /* * Memory region callbacks */ @@ -77,6 +88,17 @@ struct MemoryRegionOps { uint64_t data, unsigned size); + MemTxResult (*read_with_attrs)(struct uc_struct* uc, void *opaque, + hwaddr addr, + uint64_t *data, + unsigned size, + MemTxAttrs attrs); + MemTxResult (*write_with_attrs)(struct uc_struct* uc, void *opaque, + hwaddr addr, + uint64_t data, + unsigned size, + MemTxAttrs attrs); + enum device_endian endianness; /* Guest-visible constraints: */ struct { diff --git a/qemu/ioport.c b/qemu/ioport.c index bed2c13d..6c67d647 100644 --- a/qemu/ioport.c +++ b/qemu/ioport.c @@ -57,9 +57,25 @@ static void unassigned_io_write(struct uc_struct* uc, void *opaque, hwaddr addr, { } +static MemTxResult unassigned_io_read_with_attrs(struct uc_struct* uc, void *opaque, hwaddr addr, + uint64_t *data, unsigned size, MemTxAttrs attrs) +{ + return MEMTX_OK; +} + +static MemTxResult unassigned_write_with_attrs(struct uc_struct* uc, void *opaque, + hwaddr addr, uint64_t data, unsigned size, + MemTxAttrs attrs) +{ + return MEMTX_OK; +} + + const MemoryRegionOps unassigned_io_ops = { unassigned_io_read, unassigned_io_write, + unassigned_io_read_with_attrs, + unassigned_write_with_attrs, DEVICE_NATIVE_ENDIAN, }; diff --git a/qemu/memory.c b/qemu/memory.c index 50ebefd1..e69458ae 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -406,74 +406,133 @@ static void adjust_endianness(MemoryRegion *mr, uint64_t *data, unsigned size) } } -static void memory_region_oldmmio_read_accessor(MemoryRegion *mr, - hwaddr addr, - uint64_t *value, - unsigned size, - unsigned shift, - uint64_t mask) +static MemTxResult memory_region_oldmmio_read_accessor(MemoryRegion *mr, + hwaddr addr, + uint64_t *value, + unsigned size, + unsigned shift, + uint64_t mask, + MemTxAttrs attrs) { uint64_t tmp; tmp = mr->ops->old_mmio.read[ctz32(size)](mr->opaque, addr); + // UNICORN: Commented out + //trace_memory_region_ops_read(mr, addr, tmp, size); *value |= (tmp & mask) << shift; + return MEMTX_OK; } -static void memory_region_read_accessor(MemoryRegion *mr, - hwaddr addr, - uint64_t *value, - unsigned size, - unsigned shift, - uint64_t mask) +static MemTxResult memory_region_read_accessor(MemoryRegion *mr, + hwaddr addr, + uint64_t *value, + unsigned size, + unsigned shift, + uint64_t mask, + MemTxAttrs attrs) { uint64_t tmp; + // UNICORN: Commented out + //if (mr->flush_coalesced_mmio) { + // qemu_flush_coalesced_mmio_buffer(); + //} tmp = mr->ops->read(mr->uc, mr->opaque, addr, size); *value |= (tmp & mask) << shift; + return MEMTX_OK; } -static void memory_region_oldmmio_write_accessor(MemoryRegion *mr, - hwaddr addr, - uint64_t *value, - unsigned size, - unsigned shift, - uint64_t mask) +static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr, + hwaddr addr, + uint64_t *value, + unsigned size, + unsigned shift, + uint64_t mask, + MemTxAttrs attrs) +{ + uint64_t tmp = 0; + MemTxResult r; + + // UNICORN: commented out + //if (mr->flush_coalesced_mmio) { + // qemu_flush_coalesced_mmio_buffer(); + //} + r = mr->ops->read_with_attrs(mr->uc, mr->opaque, addr, &tmp, size, attrs); + // UNICORN: Commented out + //trace_memory_region_ops_read(mr, addr, tmp, size); + *value |= (tmp & mask) << shift; + return r; +} + +static MemTxResult memory_region_oldmmio_write_accessor(MemoryRegion *mr, + hwaddr addr, + uint64_t *value, + unsigned size, + unsigned shift, + uint64_t mask, + MemTxAttrs attrs) { uint64_t tmp; tmp = (*value >> shift) & mask; mr->ops->old_mmio.write[ctz32(size)](mr->opaque, addr, tmp); + return MEMTX_OK; } -static void memory_region_write_accessor(MemoryRegion *mr, - hwaddr addr, - uint64_t *value, - unsigned size, - unsigned shift, - uint64_t mask) +static MemTxResult memory_region_write_accessor(MemoryRegion *mr, + hwaddr addr, + uint64_t *value, + unsigned size, + unsigned shift, + uint64_t mask, + MemTxAttrs attrs) { uint64_t tmp; tmp = (*value >> shift) & mask; mr->ops->write(mr->uc, mr->opaque, addr, tmp, size); + return MEMTX_OK; } -static void access_with_adjusted_size(hwaddr addr, +static MemTxResult memory_region_write_with_attrs_accessor(MemoryRegion *mr, + hwaddr addr, + uint64_t *value, + unsigned size, + unsigned shift, + uint64_t mask, + MemTxAttrs attrs) +{ + uint64_t tmp; + + // UNICORN: Commented out + //if (mr->flush_coalesced_mmio) { + // qemu_flush_coalesced_mmio_buffer(); + //} + tmp = (*value >> shift) & mask; + // UNICORN: Commented out + //trace_memory_region_ops_write(mr, addr, tmp, size); + return mr->ops->write_with_attrs(mr->uc, mr->opaque, addr, tmp, size, attrs); +} + +static MemTxResult access_with_adjusted_size(hwaddr addr, uint64_t *value, unsigned size, unsigned access_size_min, unsigned access_size_max, - void (*access)(MemoryRegion *mr, - hwaddr addr, - uint64_t *value, - unsigned size, - unsigned shift, - uint64_t mask), - MemoryRegion *mr) + MemTxResult (*access)(MemoryRegion *mr, + hwaddr addr, + uint64_t *value, + unsigned size, + unsigned shift, + uint64_t mask, + MemTxAttrs attrs), + MemoryRegion *mr, + MemTxAttrs attrs) { uint64_t access_mask; unsigned access_size; unsigned i; + MemTxResult r = MEMTX_OK; if (!access_size_min) { access_size_min = 1; @@ -487,14 +546,16 @@ static void access_with_adjusted_size(hwaddr addr, access_mask = (0-1ULL) >> (64 - access_size * 8); if (memory_region_big_endian(mr)) { for (i = 0; i < size; i += access_size) { - access(mr, addr + i, value, access_size, - (size - access_size - i) * 8, access_mask); + r |= access(mr, addr + i, value, access_size, + (size - access_size - i) * 8, access_mask, attrs); } } else { for (i = 0; i < size; i += access_size) { - access(mr, addr + i, value, access_size, i * 8, access_mask); + r |= access(mr, addr + i, value, access_size, i * 8, + access_mask, attrs); } } + return r; } static AddressSpace *memory_region_to_address_space(MemoryRegion *mr) @@ -944,7 +1005,8 @@ static bool unassigned_mem_accepts(void *opaque, hwaddr addr, const MemoryRegionOps unassigned_mem_ops = { NULL, NULL, - + NULL, + NULL, DEVICE_NATIVE_ENDIAN, {0,0,false,unassigned_mem_accepts}, @@ -987,62 +1049,82 @@ bool memory_region_access_valid(MemoryRegion *mr, return true; } -static uint64_t memory_region_dispatch_read1(MemoryRegion *mr, - hwaddr addr, - unsigned size) +static MemTxResult memory_region_dispatch_read1(MemoryRegion *mr, + hwaddr addr, + uint64_t *pval, + unsigned size, + MemTxAttrs attrs) { - uint64_t data = 0; + *pval = 0; if (mr->ops->read) { - access_with_adjusted_size(addr, &data, size, - mr->ops->impl.min_access_size, - mr->ops->impl.max_access_size, - memory_region_read_accessor, mr); + return access_with_adjusted_size(addr, pval, size, + mr->ops->impl.min_access_size, + mr->ops->impl.max_access_size, + memory_region_read_accessor, + mr, attrs); + } else if (mr->ops->read_with_attrs) { + return access_with_adjusted_size(addr, pval, size, + mr->ops->impl.min_access_size, + mr->ops->impl.max_access_size, + memory_region_read_with_attrs_accessor, + mr, attrs); } else { - access_with_adjusted_size(addr, &data, size, 1, 4, - memory_region_oldmmio_read_accessor, mr); + return access_with_adjusted_size(addr, pval, size, 1, 4, + memory_region_oldmmio_read_accessor, + mr, attrs); } - - return data; } -static bool memory_region_dispatch_read(MemoryRegion *mr, - hwaddr addr, - uint64_t *pval, - unsigned size) +static MemTxResult memory_region_dispatch_read(MemoryRegion *mr, + hwaddr addr, + uint64_t *pval, + unsigned size, + MemTxAttrs attrs) { + MemTxResult r; + if (!memory_region_access_valid(mr, addr, size, false)) { *pval = unassigned_mem_read(mr->uc, addr, size); - return true; + return MEMTX_DECODE_ERROR; } - *pval = memory_region_dispatch_read1(mr, addr, size); + r = memory_region_dispatch_read1(mr, addr, pval, size, attrs); adjust_endianness(mr, pval, size); - return false; + return r; } -static bool memory_region_dispatch_write(MemoryRegion *mr, - hwaddr addr, - uint64_t data, - unsigned size) +static MemTxResult memory_region_dispatch_write(MemoryRegion *mr, + hwaddr addr, + uint64_t data, + unsigned size, + MemTxAttrs attrs) { if (!memory_region_access_valid(mr, addr, size, true)) { unassigned_mem_write(mr->uc, addr, data, size); - return true; + return MEMTX_DECODE_ERROR; } adjust_endianness(mr, &data, size); if (mr->ops->write) { - access_with_adjusted_size(addr, &data, size, - mr->ops->impl.min_access_size, - mr->ops->impl.max_access_size, - memory_region_write_accessor, mr); + return access_with_adjusted_size(addr, &data, size, + mr->ops->impl.min_access_size, + mr->ops->impl.max_access_size, + memory_region_write_accessor, mr, + attrs); + } else if (mr->ops->write_with_attrs) { + return + access_with_adjusted_size(addr, &data, size, + mr->ops->impl.min_access_size, + mr->ops->impl.max_access_size, + memory_region_write_with_attrs_accessor, + mr, attrs); } else { - access_with_adjusted_size(addr, &data, size, 1, 4, - memory_region_oldmmio_write_accessor, mr); + return access_with_adjusted_size(addr, &data, size, 1, 4, + memory_region_oldmmio_write_accessor, + mr, attrs); } - return false; } void memory_region_init_io(struct uc_struct *uc, MemoryRegion *mr, @@ -1578,13 +1660,15 @@ void address_space_destroy(AddressSpace *as) bool io_mem_read(MemoryRegion *mr, hwaddr addr, uint64_t *pval, unsigned size) { - return memory_region_dispatch_read(mr, addr, pval, size); + return memory_region_dispatch_read(mr, addr, pval, size, + MEMTXATTRS_UNSPECIFIED); } bool io_mem_write(MemoryRegion *mr, hwaddr addr, uint64_t val, unsigned size) { - return memory_region_dispatch_write(mr, addr, val, size); + return memory_region_dispatch_write(mr, addr, val, size, + MEMTXATTRS_UNSPECIFIED); } typedef struct MemoryRegionList MemoryRegionList;