* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "sysemu/dma.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "trace.h"
+#include "qom/object.h"
#ifndef PL330_ERR_DEBUG
#define PL330_ERR_DEBUG 0
#endif
-#define DB_PRINT_L(lvl, fmt, args...) do {\
- if (PL330_ERR_DEBUG >= lvl) {\
- fprintf(stderr, "PL330: %s:" fmt, __func__, ## args);\
- } \
-} while (0);
-
-#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
-
#define PL330_PERIPH_NUM 32
#define PL330_MAX_BURST_LEN 128
#define PL330_INSN_MAXSIZE 6
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
- VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, 0, buf_size),
- VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, 0, buf_size),
+ VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, buf_size),
+ VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, buf_size),
VMSTATE_UINT32(head, PL330Fifo),
VMSTATE_UINT32(num, PL330Fifo),
VMSTATE_UINT32(buf_size, PL330Fifo),
static const VMStateDescription vmstate_pl330_queue = {
.name = "pl330_queue",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
- VMSTATE_STRUCT_VARRAY_UINT32(queue, PL330Queue, queue_size, 1,
- vmstate_pl330_queue_entry, PL330QueueEntry),
+ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(queue, PL330Queue, queue_size,
+ vmstate_pl330_queue_entry,
+ PL330QueueEntry),
VMSTATE_END_OF_LIST()
}
};
uint8_t num_faulting;
uint8_t periph_busy[PL330_PERIPH_NUM];
+ /* Memory region that DMA operation access */
+ MemoryRegion *mem_mr;
+ AddressSpace *mem_as;
};
#define TYPE_PL330 "pl330"
-#define PL330(obj) OBJECT_CHECK(PL330State, (obj), TYPE_PL330)
+OBJECT_DECLARE_SIMPLE_TYPE(PL330State, PL330)
static const VMStateDescription vmstate_pl330 = {
.name = "pl330",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan),
- VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0,
- vmstate_pl330_chan, PL330Chan),
- VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, 0, num_chnls),
- VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, 0, num_chnls),
+ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(chan, PL330State, num_chnls,
+ vmstate_pl330_chan, PL330Chan),
+ VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, num_chnls),
+ VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, num_chnls),
VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo),
VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue,
PL330Queue),
VMSTATE_STRUCT(write_queue, PL330State, 0, vmstate_pl330_queue,
PL330Queue),
- VMSTATE_TIMER(timer, PL330State),
+ VMSTATE_TIMER_PTR(timer, PL330State),
VMSTATE_UINT32(inten, PL330State),
VMSTATE_UINT32(int_status, PL330State),
VMSTATE_UINT32(ev_status, PL330State),
void (*exec)(PL330Chan *, uint8_t opcode, uint8_t *args, int len);
} PL330InsnDesc;
+static void pl330_hexdump(uint8_t *buf, size_t size)
+{
+ unsigned int b, i, len;
+ char tmpbuf[80];
+
+ for (b = 0; b < size; b += 16) {
+ len = size - b;
+ if (len > 16) {
+ len = 16;
+ }
+ tmpbuf[0] = '\0';
+ for (i = 0; i < len; i++) {
+ if ((i % 4) == 0) {
+ strcat(tmpbuf, " ");
+ }
+ sprintf(tmpbuf + strlen(tmpbuf), " %02x", buf[b + i]);
+ }
+ trace_pl330_hexdump(b, tmpbuf);
+ }
+}
/* MFIFO Implementation
*
static inline void pl330_fault(PL330Chan *ch, uint32_t flags)
{
- DB_PRINT("ch: %p, flags: %" PRIx32 "\n", ch, flags);
+ trace_pl330_fault(ch, flags);
ch->fault_type |= flags;
if (ch->state == pl330_chan_fault) {
return;
ch->state = pl330_chan_fault;
ch->parent->num_faulting++;
if (ch->parent->num_faulting == 1) {
- DB_PRINT("abort interrupt raised\n");
+ trace_pl330_fault_abort();
qemu_irq_raise(ch->parent->irq_abort);
}
}
return;
}
}
- DB_PRINT("DMA ending!\n");
+ trace_pl330_dmaend();
pl330_fifo_tagged_remove(&s->fifo, ch->tag);
pl330_queue_remove_tagged(&s->read_queue, ch->tag);
pl330_queue_remove_tagged(&s->write_queue, ch->tag);
uint32_t pc;
PL330Chan *s;
- DB_PRINT("\n");
+ trace_pl330_dmago();
if (!ch->is_manager) {
pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src,
size, num, inc, 0, ch->tag);
if (!ch->stall) {
- DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32
- " num:%" PRId32 " %c\n",
- ch->tag, ch->src, size, num, inc ? 'Y' : 'N');
+ trace_pl330_dmald(ch->tag, ch->src, size, num, inc ? 'Y' : 'N');
ch->src += inc ? size * num - (ch->src & (size - 1)) : 0;
}
}
ch->fault_type = 0;
ch->parent->num_faulting--;
if (ch->parent->num_faulting == 0) {
- DB_PRINT("abort interrupt lowered\n");
+ trace_pl330_dmakill();
qemu_irq_lower(ch->parent->irq_abort);
}
}
uint8_t bs = opcode & 3;
uint8_t lc = (opcode & 4) >> 2;
+ trace_pl330_dmalpend(nf, bs, lc, ch->lc[lc], ch->request_flag);
+
if (bs == 2) {
pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
return;
if (nf) {
ch->lc[lc]--;
}
- DB_PRINT("loop reiteration\n");
+ trace_pl330_dmalpiter();
ch->pc -= args[0];
ch->pc -= len + 1;
/* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */
} else {
- DB_PRINT("loop fallthrough\n");
+ trace_pl330_dmalpfallthrough();
}
}
}
if (ch->parent->inten & (1 << ev_id)) {
ch->parent->int_status |= (1 << ev_id);
- DB_PRINT("event interrupt raised %" PRId8 "\n", ev_id);
+ trace_pl330_dmasev_evirq(ev_id);
qemu_irq_raise(ch->parent->irq[ev_id]);
}
- DB_PRINT("event raised %" PRId8 "\n", ev_id);
+ trace_pl330_dmasev_event(ev_id);
ch->parent->ev_status |= (1 << ev_id);
}
ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst,
size, num, inc, 0, ch->tag);
if (!ch->stall) {
- DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32
- " num:%" PRId32 " %c\n",
- ch->tag, ch->dst, size, num, inc ? 'Y' : 'N');
+ trace_pl330_dmast(ch->tag, ch->dst, size, num, inc ? 'Y' : 'N');
ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0;
}
}
}
}
ch->parent->ev_status &= ~(1 << ev_id);
- DB_PRINT("event lowered %" PRIx8 "\n", ev_id);
+ trace_pl330_dmawfe(ev_id);
} else {
ch->stall = 1;
}
uint8_t opcode;
int i;
- dma_memory_read(&address_space_memory, ch->pc, &opcode, 1);
+ dma_memory_read(ch->parent->mem_as, ch->pc, &opcode, 1,
+ MEMTXATTRS_UNSPECIFIED);
for (i = 0; insn_desc[i].size; i++) {
if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode) {
return &insn_desc[i];
uint8_t buf[PL330_INSN_MAXSIZE];
assert(insn->size <= PL330_INSN_MAXSIZE);
- dma_memory_read(&address_space_memory, ch->pc, buf, insn->size);
+ dma_memory_read(ch->parent->mem_as, ch->pc, buf, insn->size,
+ MEMTXATTRS_UNSPECIFIED);
insn->exec(ch, buf[0], &buf[1], insn->size - 1);
}
ch->stall = 0;
insn = pl330_fetch_insn(ch);
if (!insn) {
- DB_PRINT("pl330 undefined instruction\n");
+ trace_pl330_chan_exec_undef();
pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
return 0;
}
if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) {
int len = q->len - (q->addr & (q->len - 1));
- dma_memory_read(&address_space_memory, q->addr, buf, len);
- if (PL330_ERR_DEBUG > 1) {
- DB_PRINT("PL330 read from memory @%08" PRIx32 " (size = %08x):\n",
- q->addr, len);
- qemu_hexdump((char *)buf, stderr, "", len);
+ dma_memory_read(s->mem_as, q->addr, buf, len,
+ MEMTXATTRS_UNSPECIFIED);
+ trace_pl330_exec_cycle(q->addr, len);
+ if (trace_event_get_state_backends(TRACE_PL330_HEXDUMP)) {
+ pl330_hexdump(buf, len);
}
fifo_res = pl330_fifo_push(&s->fifo, buf, len, q->tag);
if (fifo_res == PL330_FIFO_OK) {
fifo_res = pl330_fifo_get(&s->fifo, buf, len, q->tag);
}
if (fifo_res == PL330_FIFO_OK || q->z) {
- dma_memory_write(&address_space_memory, q->addr, buf, len);
- if (PL330_ERR_DEBUG > 1) {
- DB_PRINT("PL330 read from memory @%08" PRIx32
- " (size = %08x):\n", q->addr, len);
- qemu_hexdump((char *)buf, stderr, "", len);
+ dma_memory_write(s->mem_as, q->addr, buf, len,
+ MEMTXATTRS_UNSPECIFIED);
+ trace_pl330_exec_cycle(q->addr, len);
+ if (trace_event_get_state_backends(TRACE_PL330_HEXDUMP)) {
+ pl330_hexdump(buf, len);
}
if (q->inc) {
q->addr += len;
static inline void pl330_exec(PL330State *s)
{
- DB_PRINT("\n");
int i, insr_exec;
+ trace_pl330_exec();
do {
insr_exec = pl330_exec_channel(&s->manager);
args[2] = (s->dbg[1] >> 8) & 0xff;
args[3] = (s->dbg[1] >> 16) & 0xff;
args[4] = (s->dbg[1] >> 24) & 0xff;
- DB_PRINT("chan id: %" PRIx8 "\n", chan_id);
+ trace_pl330_debug_exec(chan_id);
if (s->dbg[0] & 1) {
ch = &s->chan[chan_id];
} else {
ch->fault_type |= PL330_FAULT_DBG_INSTR;
}
if (ch->stall) {
+ trace_pl330_debug_exec_stall();
qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not "
"implemented\n");
}
PL330State *s = (PL330State *) opaque;
int i;
- DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value);
+ trace_pl330_iomem_write((unsigned)offset, (unsigned)value);
switch (offset) {
case PL330_REG_INTEN:
case PL330_REG_INTCLR:
for (i = 0; i < s->num_events; i++) {
if (s->int_status & s->inten & value & (1 << i)) {
- DB_PRINT("event interrupt lowered %d\n", i);
+ trace_pl330_iomem_write_clr(i);
qemu_irq_lower(s->irq[i]);
}
}
}
break;
case PL330_REG_DBGINST0:
- DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value);
s->dbg[0] = value;
break;
case PL330_REG_DBGINST1:
- DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value);
s->dbg[1] = value;
break;
default:
unsigned size)
{
uint32_t ret = pl330_iomem_read_imp(opaque, offset);
- DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset, ret);
+ trace_pl330_iomem_read((uint32_t)offset, ret);
return ret;
}
"dma", PL330_IOMEM_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+ if (!s->mem_mr) {
+ error_setg(errp, "'memory' link is not set");
+ return;
+ } else if (s->mem_mr == get_system_memory()) {
+ /* Avoid creating new AS for system memory. */
+ s->mem_as = &address_space_memory;
+ } else {
+ s->mem_as = g_new0(AddressSpace, 1);
+ address_space_init(s->mem_as, s->mem_mr,
+ memory_region_name(s->mem_mr));
+ }
+
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pl330_exec_cycle_timer, s);
s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) |
s->cfg[1] |= 5;
break;
default:
- error_setg(errp, "Bad value for i-cache_len property: %" PRIx8 "\n",
+ error_setg(errp, "Bad value for i-cache_len property: %" PRIx8,
s->i_cache_len);
return;
}
s->cfg[CFG_CRD] |= 0x4;
break;
default:
- error_setg(errp, "Bad value for data_width property: %" PRIx8 "\n",
+ error_setg(errp, "Bad value for data_width property: %" PRIx8,
s->data_width);
return;
}
DEFINE_PROP_UINT8("rd_q_dep", PL330State, rd_q_dep, 16),
DEFINE_PROP_UINT16("data_buffer_dep", PL330State, data_buffer_dep, 256),
+ DEFINE_PROP_LINK("memory", PL330State, mem_mr,
+ TYPE_MEMORY_REGION, MemoryRegion *),
+
DEFINE_PROP_END_OF_LIST(),
};
dc->realize = pl330_realize;
dc->reset = pl330_reset;
- dc->props = pl330_properties;
+ device_class_set_props(dc, pl330_properties);
dc->vmsd = &vmstate_pl330;
}