Message ID | 1518893216-9983-8-git-send-email-eric.auger@redhat.com |
---|---|
State | New |
Headers | show |
Series | ARM SMMUv3 Emulation Support | expand |
On 17 February 2018 at 18:46, Eric Auger <eric.auger@redhat.com> wrote: > Now we have relevant helpers for queue and irq > management, let's implement MMIO write operations. > > Signed-off-by: Eric Auger <eric.auger@redhat.com> > > --- > > v7 -> v8: > - precise in the commit message invalidation commands > are not yet treated. > - use new queue helpers > - do not decode unhandled commands at this stage > --- > hw/arm/smmuv3-internal.h | 24 +++++++--- > hw/arm/smmuv3.c | 111 +++++++++++++++++++++++++++++++++++++++++++++-- > hw/arm/trace-events | 6 +++ > 3 files changed, 132 insertions(+), 9 deletions(-) > > diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h > index c0771ce..5af97ae 100644 > --- a/hw/arm/smmuv3-internal.h > +++ b/hw/arm/smmuv3-internal.h > @@ -152,6 +152,25 @@ static inline uint64_t smmu_read64(uint64_t r, unsigned offset, > return extract64(r, offset << 3, 32); > } > > +static inline void smmu_write64(uint64_t *r, unsigned offset, > + unsigned size, uint64_t value) > +{ > + if (size == 8 && !offset) { > + *r = value; > + } > + > + /* 32 bit access */ > + > + if (offset && offset != 4) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "SMMUv3 MMIO write: bad offset/size %u/%u\n", > + offset, size); > + return ; > + } > + > + *r = deposit64(*r, offset << 3, 32, value); Similar remarks apply to this helper as to smmu_read64 in the earlier patch. > +} > + > /* Interrupts */ > > #define smmuv3_eventq_irq_enabled(s) \ > @@ -159,9 +178,6 @@ static inline uint64_t smmu_read64(uint64_t r, unsigned offset, > #define smmuv3_gerror_irq_enabled(s) \ > (FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN)) > > -void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask); > -void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t gerrorn); > - > /* Queue Handling */ > > #define LOG2SIZE(q) extract64((q)->base, 0, 5) > @@ -310,6 +326,4 @@ enum { /* Command completion notification */ > addr; \ > }) > > -int smmuv3_cmdq_consume(SMMUv3State *s); > - > #endif > diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c > index 0b57215..fcfdbb0 100644 > --- a/hw/arm/smmuv3.c > +++ b/hw/arm/smmuv3.c > @@ -37,7 +37,8 @@ > * @irq: irq type > * @gerror_mask: mask of gerrors to toggle (relevant if @irq is GERROR) > */ > -void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) > +static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, > + uint32_t gerror_mask) > { > > bool pulse = false; > @@ -75,7 +76,7 @@ void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) > } > } > > -void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) > +static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) > { > uint32_t pending = s->gerror ^ s->gerrorn; > uint32_t toggled = s->gerrorn ^ new_gerrorn; > @@ -199,7 +200,7 @@ static void smmuv3_init_regs(SMMUv3State *s) > s->sid_split = 0; > } > > -int smmuv3_cmdq_consume(SMMUv3State *s) > +static int smmuv3_cmdq_consume(SMMUv3State *s) > { > SMMUCmdError cmd_error = SMMU_CERROR_NONE; > SMMUQueue *q = &s->cmdq; > @@ -298,7 +299,109 @@ int smmuv3_cmdq_consume(SMMUv3State *s) > static void smmu_write_mmio(void *opaque, hwaddr addr, > uint64_t val, unsigned size) > { > - /* not yet implemented */ > + SMMUState *sys = opaque; > + SMMUv3State *s = ARM_SMMUV3(sys); > + > + /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */ > + addr &= ~0x10000; > + > + if (size != 4 && size != 8) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "SMMUv3 MMIO write: bad size %u\n", size); > + } As with read, this can never happen so you don't need to check for it. As with read, probably better to explicitly whitelist the 64-bit accessible offsets, and LOG_GUEST_ERROR and write-ignore the others. > + > + trace_smmuv3_write_mmio(addr, val, size); > + > + switch (addr) { > + case A_CR0: > + s->cr[0] = val; > + s->cr0ack = val; Spec says "reserved fields in SMMU_CR0 are not reflected in SMMU_CR0ACK", so you probably need to mask those out. > + /* in case the command queue has been enabled */ > + smmuv3_cmdq_consume(s); > + return; > + case A_CR1: > + s->cr[1] = val; > + return; > + case A_CR2: > + s->cr[2] = val; > + return; > + case A_IRQ_CTRL: > + s->irq_ctrl = val; > + return; > + case A_GERRORN: > + smmuv3_write_gerrorn(s, val); > + /* > + * By acknowledging the CMDQ_ERR, SW may notify cmds can > + * be processed again > + */ > + smmuv3_cmdq_consume(s); > + return; > + case A_GERROR_IRQ_CFG0: /* 64b */ > + smmu_write64(&s->gerror_irq_cfg0, 0, size, val); > + return; > + case A_GERROR_IRQ_CFG0 + 4: > + smmu_write64(&s->gerror_irq_cfg0, 4, size, val); > + return; > + case A_GERROR_IRQ_CFG1: > + s->gerror_irq_cfg1 = val; > + return; > + case A_GERROR_IRQ_CFG2: > + s->gerror_irq_cfg2 = val; > + return; > + case A_STRTAB_BASE: /* 64b */ > + smmu_write64(&s->strtab_base, 0, size, val); > + return; > + case A_STRTAB_BASE + 4: > + smmu_write64(&s->strtab_base, 4, size, val); > + return; > + case A_STRTAB_BASE_CFG: > + s->strtab_base_cfg = val; > + if (FIELD_EX32(val, STRTAB_BASE_CFG, FMT) == 1) { > + s->sid_split = FIELD_EX32(val, STRTAB_BASE_CFG, SPLIT); > + s->features |= SMMU_FEATURE_2LVL_STE; > + } > + return; > + case A_CMDQ_BASE: /* 64b */ > + smmu_write64(&s->cmdq.base, 0, size, val); > + return; > + case A_CMDQ_BASE + 4: /* 64b */ > + smmu_write64(&s->cmdq.base, 4, size, val); > + return; > + case A_CMDQ_PROD: > + s->cmdq.prod = val; > + smmuv3_cmdq_consume(s); > + return; > + case A_CMDQ_CONS: > + s->cmdq.cons = val; > + return; > + case A_EVENTQ_BASE: /* 64b */ > + smmu_write64(&s->eventq.base, 0, size, val); > + return; > + case A_EVENTQ_BASE + 4: > + smmu_write64(&s->eventq.base, 4, size, val); > + return; > + case A_EVENTQ_PROD: > + s->eventq.prod = val; > + return; > + case A_EVENTQ_CONS: > + s->eventq.cons = val; > + return; > + case A_EVENTQ_IRQ_CFG0: /* 64b */ > + s->eventq.prod = val; > + smmu_write64(&s->eventq_irq_cfg0, 0, size, val); > + return; > + case A_EVENTQ_IRQ_CFG0 + 4: > + smmu_write64(&s->eventq_irq_cfg0, 4, size, val); > + return; > + case A_EVENTQ_IRQ_CFG1: > + s->eventq_irq_cfg1 = val; > + return; > + case A_EVENTQ_IRQ_CFG2: > + s->eventq_irq_cfg2 = val; > + return; > + default: > + error_report("%s unhandled access at 0x%"PRIx64, __func__, addr); Tracepoint or LOG_GUEST_ERROR, not error_report(), please. > + } > } > > static uint64_t smmu_read_mmio(void *opaque, hwaddr addr, unsigned size) > diff --git a/hw/arm/trace-events b/hw/arm/trace-events > index 1c5105d..ed5dce0 100644 > --- a/hw/arm/trace-events > +++ b/hw/arm/trace-events > @@ -22,3 +22,9 @@ smmuv3_unhandled_cmd(uint32_t type) "Unhandled command type=%d" > smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod=%d cons=%d prod.wrap=%d cons.wrap=%d" > smmuv3_cmdq_opcode(const char *opcode) "<--- %s" > smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d " > +smmuv3_update(bool is_empty, uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d" > +smmuv3_update_check_cmd(int error) "cmdq not enabled or error :0x%x" > +smmuv3_write_mmio(hwaddr addr, uint64_t val, unsigned size) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x" > +smmuv3_write_mmio_idr(hwaddr addr, uint64_t val) "write to RO/Unimpl reg 0x%lx val64:0x%lx" > +smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" > +smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" thanks -- PMM
Hi Peter, On 08/03/18 19:37, Peter Maydell wrote: > On 17 February 2018 at 18:46, Eric Auger <eric.auger@redhat.com> wrote: >> Now we have relevant helpers for queue and irq >> management, let's implement MMIO write operations. >> >> Signed-off-by: Eric Auger <eric.auger@redhat.com> >> >> --- >> >> v7 -> v8: >> - precise in the commit message invalidation commands >> are not yet treated. >> - use new queue helpers >> - do not decode unhandled commands at this stage >> --- >> hw/arm/smmuv3-internal.h | 24 +++++++--- >> hw/arm/smmuv3.c | 111 +++++++++++++++++++++++++++++++++++++++++++++-- >> hw/arm/trace-events | 6 +++ >> 3 files changed, 132 insertions(+), 9 deletions(-) >> >> diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h >> index c0771ce..5af97ae 100644 >> --- a/hw/arm/smmuv3-internal.h >> +++ b/hw/arm/smmuv3-internal.h >> @@ -152,6 +152,25 @@ static inline uint64_t smmu_read64(uint64_t r, unsigned offset, >> return extract64(r, offset << 3, 32); >> } >> >> +static inline void smmu_write64(uint64_t *r, unsigned offset, >> + unsigned size, uint64_t value) >> +{ >> + if (size == 8 && !offset) { >> + *r = value; >> + } >> + >> + /* 32 bit access */ >> + >> + if (offset && offset != 4) { >> + qemu_log_mask(LOG_GUEST_ERROR, >> + "SMMUv3 MMIO write: bad offset/size %u/%u\n", >> + offset, size); >> + return ; >> + } >> + >> + *r = deposit64(*r, offset << 3, 32, value); > > Similar remarks apply to this helper as to smmu_read64 in the earlier patch. removed > >> +} >> + >> /* Interrupts */ >> >> #define smmuv3_eventq_irq_enabled(s) \ >> @@ -159,9 +178,6 @@ static inline uint64_t smmu_read64(uint64_t r, unsigned offset, >> #define smmuv3_gerror_irq_enabled(s) \ >> (FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN)) >> >> -void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask); >> -void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t gerrorn); >> - >> /* Queue Handling */ >> >> #define LOG2SIZE(q) extract64((q)->base, 0, 5) >> @@ -310,6 +326,4 @@ enum { /* Command completion notification */ >> addr; \ >> }) >> >> -int smmuv3_cmdq_consume(SMMUv3State *s); >> - >> #endif >> diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c >> index 0b57215..fcfdbb0 100644 >> --- a/hw/arm/smmuv3.c >> +++ b/hw/arm/smmuv3.c >> @@ -37,7 +37,8 @@ >> * @irq: irq type >> * @gerror_mask: mask of gerrors to toggle (relevant if @irq is GERROR) >> */ >> -void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) >> +static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, >> + uint32_t gerror_mask) >> { >> >> bool pulse = false; >> @@ -75,7 +76,7 @@ void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) >> } >> } >> >> -void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) >> +static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) >> { >> uint32_t pending = s->gerror ^ s->gerrorn; >> uint32_t toggled = s->gerrorn ^ new_gerrorn; >> @@ -199,7 +200,7 @@ static void smmuv3_init_regs(SMMUv3State *s) >> s->sid_split = 0; >> } >> >> -int smmuv3_cmdq_consume(SMMUv3State *s) >> +static int smmuv3_cmdq_consume(SMMUv3State *s) >> { >> SMMUCmdError cmd_error = SMMU_CERROR_NONE; >> SMMUQueue *q = &s->cmdq; >> @@ -298,7 +299,109 @@ int smmuv3_cmdq_consume(SMMUv3State *s) >> static void smmu_write_mmio(void *opaque, hwaddr addr, >> uint64_t val, unsigned size) >> { >> - /* not yet implemented */ >> + SMMUState *sys = opaque; >> + SMMUv3State *s = ARM_SMMUV3(sys); >> + >> + /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */ >> + addr &= ~0x10000; >> + >> + if (size != 4 && size != 8) { >> + qemu_log_mask(LOG_GUEST_ERROR, >> + "SMMUv3 MMIO write: bad size %u\n", size); >> + } > > As with read, this can never happen so you don't need to check for it. > > As with read, probably better to explicitly whitelist the 64-bit > accessible offsets, and LOG_GUEST_ERROR and write-ignore the others. done > >> + >> + trace_smmuv3_write_mmio(addr, val, size); >> + >> + switch (addr) { >> + case A_CR0: >> + s->cr[0] = val; >> + s->cr0ack = val; > > Spec says "reserved fields in SMMU_CR0 are not reflected in SMMU_CR0ACK", > so you probably need to mask those out. OK > >> + /* in case the command queue has been enabled */ >> + smmuv3_cmdq_consume(s); >> + return; >> + case A_CR1: >> + s->cr[1] = val; >> + return; >> + case A_CR2: >> + s->cr[2] = val; >> + return; >> + case A_IRQ_CTRL: >> + s->irq_ctrl = val; >> + return; >> + case A_GERRORN: >> + smmuv3_write_gerrorn(s, val); >> + /* >> + * By acknowledging the CMDQ_ERR, SW may notify cmds can >> + * be processed again >> + */ >> + smmuv3_cmdq_consume(s); >> + return; >> + case A_GERROR_IRQ_CFG0: /* 64b */ >> + smmu_write64(&s->gerror_irq_cfg0, 0, size, val); >> + return; >> + case A_GERROR_IRQ_CFG0 + 4: >> + smmu_write64(&s->gerror_irq_cfg0, 4, size, val); >> + return; >> + case A_GERROR_IRQ_CFG1: >> + s->gerror_irq_cfg1 = val; >> + return; >> + case A_GERROR_IRQ_CFG2: >> + s->gerror_irq_cfg2 = val; >> + return; >> + case A_STRTAB_BASE: /* 64b */ >> + smmu_write64(&s->strtab_base, 0, size, val); >> + return; >> + case A_STRTAB_BASE + 4: >> + smmu_write64(&s->strtab_base, 4, size, val); >> + return; >> + case A_STRTAB_BASE_CFG: >> + s->strtab_base_cfg = val; >> + if (FIELD_EX32(val, STRTAB_BASE_CFG, FMT) == 1) { >> + s->sid_split = FIELD_EX32(val, STRTAB_BASE_CFG, SPLIT); >> + s->features |= SMMU_FEATURE_2LVL_STE; >> + } >> + return; >> + case A_CMDQ_BASE: /* 64b */ >> + smmu_write64(&s->cmdq.base, 0, size, val); >> + return; >> + case A_CMDQ_BASE + 4: /* 64b */ >> + smmu_write64(&s->cmdq.base, 4, size, val); >> + return; >> + case A_CMDQ_PROD: >> + s->cmdq.prod = val; >> + smmuv3_cmdq_consume(s); >> + return; >> + case A_CMDQ_CONS: >> + s->cmdq.cons = val; >> + return; >> + case A_EVENTQ_BASE: /* 64b */ >> + smmu_write64(&s->eventq.base, 0, size, val); >> + return; >> + case A_EVENTQ_BASE + 4: >> + smmu_write64(&s->eventq.base, 4, size, val); >> + return; >> + case A_EVENTQ_PROD: >> + s->eventq.prod = val; >> + return; >> + case A_EVENTQ_CONS: >> + s->eventq.cons = val; >> + return; >> + case A_EVENTQ_IRQ_CFG0: /* 64b */ >> + s->eventq.prod = val; >> + smmu_write64(&s->eventq_irq_cfg0, 0, size, val); >> + return; >> + case A_EVENTQ_IRQ_CFG0 + 4: >> + smmu_write64(&s->eventq_irq_cfg0, 4, size, val); >> + return; >> + case A_EVENTQ_IRQ_CFG1: >> + s->eventq_irq_cfg1 = val; >> + return; >> + case A_EVENTQ_IRQ_CFG2: >> + s->eventq_irq_cfg2 = val; >> + return; >> + default: >> + error_report("%s unhandled access at 0x%"PRIx64, __func__, addr); > > Tracepoint or LOG_GUEST_ERROR, not error_report(), please. OK Thanks Eric > >> + } >> } >> >> static uint64_t smmu_read_mmio(void *opaque, hwaddr addr, unsigned size) >> diff --git a/hw/arm/trace-events b/hw/arm/trace-events >> index 1c5105d..ed5dce0 100644 >> --- a/hw/arm/trace-events >> +++ b/hw/arm/trace-events >> @@ -22,3 +22,9 @@ smmuv3_unhandled_cmd(uint32_t type) "Unhandled command type=%d" >> smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod=%d cons=%d prod.wrap=%d cons.wrap=%d" >> smmuv3_cmdq_opcode(const char *opcode) "<--- %s" >> smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d " >> +smmuv3_update(bool is_empty, uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d" >> +smmuv3_update_check_cmd(int error) "cmdq not enabled or error :0x%x" >> +smmuv3_write_mmio(hwaddr addr, uint64_t val, unsigned size) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x" >> +smmuv3_write_mmio_idr(hwaddr addr, uint64_t val) "write to RO/Unimpl reg 0x%lx val64:0x%lx" >> +smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" >> +smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" > > > thanks > -- PMM >
diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index c0771ce..5af97ae 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -152,6 +152,25 @@ static inline uint64_t smmu_read64(uint64_t r, unsigned offset, return extract64(r, offset << 3, 32); } +static inline void smmu_write64(uint64_t *r, unsigned offset, + unsigned size, uint64_t value) +{ + if (size == 8 && !offset) { + *r = value; + } + + /* 32 bit access */ + + if (offset && offset != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "SMMUv3 MMIO write: bad offset/size %u/%u\n", + offset, size); + return ; + } + + *r = deposit64(*r, offset << 3, 32, value); +} + /* Interrupts */ #define smmuv3_eventq_irq_enabled(s) \ @@ -159,9 +178,6 @@ static inline uint64_t smmu_read64(uint64_t r, unsigned offset, #define smmuv3_gerror_irq_enabled(s) \ (FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN)) -void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask); -void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t gerrorn); - /* Queue Handling */ #define LOG2SIZE(q) extract64((q)->base, 0, 5) @@ -310,6 +326,4 @@ enum { /* Command completion notification */ addr; \ }) -int smmuv3_cmdq_consume(SMMUv3State *s); - #endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 0b57215..fcfdbb0 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -37,7 +37,8 @@ * @irq: irq type * @gerror_mask: mask of gerrors to toggle (relevant if @irq is GERROR) */ -void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) +static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, + uint32_t gerror_mask) { bool pulse = false; @@ -75,7 +76,7 @@ void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask) } } -void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) +static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) { uint32_t pending = s->gerror ^ s->gerrorn; uint32_t toggled = s->gerrorn ^ new_gerrorn; @@ -199,7 +200,7 @@ static void smmuv3_init_regs(SMMUv3State *s) s->sid_split = 0; } -int smmuv3_cmdq_consume(SMMUv3State *s) +static int smmuv3_cmdq_consume(SMMUv3State *s) { SMMUCmdError cmd_error = SMMU_CERROR_NONE; SMMUQueue *q = &s->cmdq; @@ -298,7 +299,109 @@ int smmuv3_cmdq_consume(SMMUv3State *s) static void smmu_write_mmio(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - /* not yet implemented */ + SMMUState *sys = opaque; + SMMUv3State *s = ARM_SMMUV3(sys); + + /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */ + addr &= ~0x10000; + + if (size != 4 && size != 8) { + qemu_log_mask(LOG_GUEST_ERROR, + "SMMUv3 MMIO write: bad size %u\n", size); + } + + trace_smmuv3_write_mmio(addr, val, size); + + switch (addr) { + case A_CR0: + s->cr[0] = val; + s->cr0ack = val; + /* in case the command queue has been enabled */ + smmuv3_cmdq_consume(s); + return; + case A_CR1: + s->cr[1] = val; + return; + case A_CR2: + s->cr[2] = val; + return; + case A_IRQ_CTRL: + s->irq_ctrl = val; + return; + case A_GERRORN: + smmuv3_write_gerrorn(s, val); + /* + * By acknowledging the CMDQ_ERR, SW may notify cmds can + * be processed again + */ + smmuv3_cmdq_consume(s); + return; + case A_GERROR_IRQ_CFG0: /* 64b */ + smmu_write64(&s->gerror_irq_cfg0, 0, size, val); + return; + case A_GERROR_IRQ_CFG0 + 4: + smmu_write64(&s->gerror_irq_cfg0, 4, size, val); + return; + case A_GERROR_IRQ_CFG1: + s->gerror_irq_cfg1 = val; + return; + case A_GERROR_IRQ_CFG2: + s->gerror_irq_cfg2 = val; + return; + case A_STRTAB_BASE: /* 64b */ + smmu_write64(&s->strtab_base, 0, size, val); + return; + case A_STRTAB_BASE + 4: + smmu_write64(&s->strtab_base, 4, size, val); + return; + case A_STRTAB_BASE_CFG: + s->strtab_base_cfg = val; + if (FIELD_EX32(val, STRTAB_BASE_CFG, FMT) == 1) { + s->sid_split = FIELD_EX32(val, STRTAB_BASE_CFG, SPLIT); + s->features |= SMMU_FEATURE_2LVL_STE; + } + return; + case A_CMDQ_BASE: /* 64b */ + smmu_write64(&s->cmdq.base, 0, size, val); + return; + case A_CMDQ_BASE + 4: /* 64b */ + smmu_write64(&s->cmdq.base, 4, size, val); + return; + case A_CMDQ_PROD: + s->cmdq.prod = val; + smmuv3_cmdq_consume(s); + return; + case A_CMDQ_CONS: + s->cmdq.cons = val; + return; + case A_EVENTQ_BASE: /* 64b */ + smmu_write64(&s->eventq.base, 0, size, val); + return; + case A_EVENTQ_BASE + 4: + smmu_write64(&s->eventq.base, 4, size, val); + return; + case A_EVENTQ_PROD: + s->eventq.prod = val; + return; + case A_EVENTQ_CONS: + s->eventq.cons = val; + return; + case A_EVENTQ_IRQ_CFG0: /* 64b */ + s->eventq.prod = val; + smmu_write64(&s->eventq_irq_cfg0, 0, size, val); + return; + case A_EVENTQ_IRQ_CFG0 + 4: + smmu_write64(&s->eventq_irq_cfg0, 4, size, val); + return; + case A_EVENTQ_IRQ_CFG1: + s->eventq_irq_cfg1 = val; + return; + case A_EVENTQ_IRQ_CFG2: + s->eventq_irq_cfg2 = val; + return; + default: + error_report("%s unhandled access at 0x%"PRIx64, __func__, addr); + } } static uint64_t smmu_read_mmio(void *opaque, hwaddr addr, unsigned size) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 1c5105d..ed5dce0 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -22,3 +22,9 @@ smmuv3_unhandled_cmd(uint32_t type) "Unhandled command type=%d" smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod=%d cons=%d prod.wrap=%d cons.wrap=%d" smmuv3_cmdq_opcode(const char *opcode) "<--- %s" smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d " +smmuv3_update(bool is_empty, uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d" +smmuv3_update_check_cmd(int error) "cmdq not enabled or error :0x%x" +smmuv3_write_mmio(hwaddr addr, uint64_t val, unsigned size) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x" +smmuv3_write_mmio_idr(hwaddr addr, uint64_t val) "write to RO/Unimpl reg 0x%lx val64:0x%lx" +smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" +smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d"
Now we have relevant helpers for queue and irq management, let's implement MMIO write operations. Signed-off-by: Eric Auger <eric.auger@redhat.com> --- v7 -> v8: - precise in the commit message invalidation commands are not yet treated. - use new queue helpers - do not decode unhandled commands at this stage --- hw/arm/smmuv3-internal.h | 24 +++++++--- hw/arm/smmuv3.c | 111 +++++++++++++++++++++++++++++++++++++++++++++-- hw/arm/trace-events | 6 +++ 3 files changed, 132 insertions(+), 9 deletions(-)