[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH 03/12 v3] xen/arm: vpl011: Add pl011 uart emulation in Xen



Hi Julien,

Thanks for your comments.

>
>
>> +static bool vpl011_reg32_check_access(int size)
>
>
> Please pass hsr_dabt in parameter rather than the size directly. Which BTW
> should have really be unsigned int.
>
ok.

>> +{
>> +    return (size == DABT_DOUBLE_WORD)? false : true;
>
>
> This could be simplified with (size != DABT_DOUBLE_WORD). Also please add a
> comment explaining why we allow all the sizes but 64-bit.
>
ok.

>> +}
>> +
>> +static void vpl011_update_spi(struct domain *d)
>
>
> Please rename this function to vpl011_update.
>
ok.
>> +{
>> +    struct vpl011_s *vpl011 = &d->arch.vpl011;
>> +
>> +    if ( vpl011->uartris & vpl011->uartimsc )
>
>
> Likely you want a todo to say we need to revisit that when the vGIC is
> handling properly level interrupt.
>
ok.

>> +        vgic_vcpu_inject_spi(d, GUEST_VPL011_SPI);
>> +}
>> +
>> +static void vpl011_read_data(struct domain *d, uint8_t *data)
>
>
> If a pointer is passed to be filled, then I would expect an error value to
> be returned.
>
> I would usually pointer if the function has to fill a structure or it is not
> possible to use the return.
>
> In this case, the value is 8-bit and you don't have return value. So return
> the value rather than passing a pointer.
>
I will return the data.


>
>> +        *data = intf->in[VPL011_RING_IDX_MASK(in_cons, intf->in)];
>> +        smp_mb();
>> +        intf->in_cons = in_cons + 1;
>> +    }
>
>
> For debugging purpose, it would be good to have a message if the ring is
> empty.
ok.

>
>> +
>> +    if ( VPL011_IN_RING_EMPTY(intf) )
>> +    {
>> +        vpl011->uartfr |= RXFE;
>> +        vpl011->uartris &= ~RXI;
>> +    }
>> +    vpl011->uartfr &= ~RXFF;
>> +    VPL011_UNLOCK(d, flags);
>> +
>> +    notify_via_xen_event_channel(d, vpl011->evtchn);
>
>
> It would be good to explain why you need to notification here.
>
On second thoughts, notification is not required here.


>
>> +        intf->out[VPL011_RING_IDX_MASK(out_prod, intf->out)] = data;
>> +        smp_wmb();
>> +        intf->out_prod = out_prod + 1;
>> +    }
>
>
> For debugging purpose, it would be useful to get a message if the ring is
> full.
>
ok.
>> +
>> +    if ( VPL011_OUT_RING_FULL(intf) )
>> +    {
>> +        vpl011->uartfr |= TXFF;
>> +        vpl011->uartris &= ~TXI;
>> +    }
>> +
>> +    vpl011->uartfr |= BUSY;
>
>
> I am not sure to understand why you set the bit BUSY here.

Since the OUT ring buffer is non-empty here, this bit is set to
indicate that there is data in the ring buffer (which is emulating a
UART transmit FIFO). As per pl011 TRM, this bit should be set if there
is data in the transmit fifo.

>
>> +
>> +    vpl011->uartfr &= ~TXFE;
>> +
>> +    VPL011_UNLOCK(d, flags);
>> +
>> +    notify_via_xen_event_channel(d, vpl011->evtchn);
>
>
> Same as the previous notify_*.
>
ok. I will add a comment on why we are sending an event here.

>> +}
>> +
>> +static int vpl011_mmio_read(struct vcpu *v, mmio_info_t *info, register_t
>> *r, void *priv)
>> +{
>> +    uint8_t ch;
>
>
> This could be restricted to the case DR.
>
ok.

>> +    struct hsr_dabt dabt = info->dabt;
>> +    int vpl011_reg = (int)(info->gpa - GUEST_PL011_BASE);
>
>
> Please use either unsigned int or uint32_t. Something would be really wrong
> if the offset is negative here.
ok.
>
>> +    struct vpl011_s *vpl011 = &v->domain->arch.vpl011;
>> +
>> +    if ( !vpl011_reg32_check_access(dabt.size) ) goto bad_width;
>
>
> Please add a comment why the check is the same for all registers.
>
ok.

>> +
>> +    switch ( vpl011_reg )
>> +    {
>> +    case DR:
>
>
> As mentioned above, you could do:
>
> {
>      uint8_t ch;
>      ....
> }
>
>> +        vpl011_read_data(v->domain, &ch);
>> +        *r = ch;
>
>
> Please use vreg_reg32_extract(...).
>
ok.

>> +        break;
>
>
> I admit I would prefer the "return 1;" here rather than "break;". This makes
> easier to follow the emulation for a given register.
>
> I would even be in favor of duplicating the "if ( !vpl011... )" in each case
> for the same reason.
Do you mean that I should repeat the vpl011_reg32_check_access() and
return for each switch case?

>
>> +
>> +    case RSR:
>> +        /* It always returns 0 as there are no physical errors. */
>> +        *r = 0;
>
>
> Note that I am ok to keep the 0 like that here.
>
>> +        break;
>> +
>> +    case FR:
>> +        *r = vreg_reg32_extract(vpl011->uartfr, info);
>> +        break;
>> +
>> +    case RIS:
>> +        *r = vreg_reg32_extract(vpl011->uartris, info);
>> +        break;
>> +
>> +    case MIS:
>> +        *r = vreg_reg32_extract(vpl011->uartris
>> +                                & vpl011->uartimsc, info);
>
>
> The coding style in Xen is to split after the &. E.g
>
> vpl011->uartris &
> vpl011->uartimsc
>
ok.

> Also, none of the pl011 register emulation is using any locking. This is
> quite surprising to me. So can you explain why?

In vpl011_mmio_read(), since pl011 registers are only read, it should
be ok to let
them be read without any locking.
>
>
>> +static int vpl011_mmio_write(struct vcpu *v, mmio_info_t *info,
>> register_t r, void *priv)
>> +{
>> +    uint8_t ch = ((struct uartdr_reg *)&r)->data;
>
>
> This should be restricted to the DR case below. But I am not sure to
> understand the purpose the structure here. It makes less readable and also
> you only use it for the write case.
There was a review comment earlier to make the typecasting more
readable because earlier I was doing r & 0xff to retrieve the data. So
I added a structure which tells the layout of the DR. I guess I can
use vreg_reg32_extract() to extract uint8_t value from register_t r.

>
>> +    struct hsr_dabt dabt = info->dabt;
>> +    int vpl011_reg = (int)(info->gpa - GUEST_PL011_BASE);
>
>
> Same as above for vpl011_reg.
ok.
>
>> +    struct vpl011_s *vpl011 = &v->domain->arch.vpl011;
>> +
>> +    if ( !vpl011_reg32_check_access(dabt.size) ) goto bad_width;
>
>
> Same as the previous vpl011_reg32_check_access... and all of my remarks in
> the read emulation stand for the write one.
>
>> +
>> +    switch ( vpl011_reg )
>> +    {
>> +    case DR:
>> +        vpl011_write_data(v->domain, ch);
>> +        break;
>> +
>> +    case RSR: /* Nothing to clear. */
>> +        break;
>> +
>> +    case FR:
>> +    case RIS:
>> +    case MIS:
>> +        goto write_ignore;
>> +
>> +    case IMSC:
>> +        vreg_reg32_update(&vpl011->uartimsc, r, info);
>> +        vpl011_update_spi(v->domain);
>
>
> I would have expected some locking here.
Yes here lock should be taken.

>
>> +        break;
>> +
>> +    case ICR:
>> +        vreg_reg32_clearbits(&vpl011->uartris, r, info);
>> +        vpl011_update_spi(v->domain);
>
>
> Same here.
Yes here lock should be taken.
>
>
>> +        break;
>> +
>> +    default:
>> +        gprintk(XENLOG_ERR, "vpl011: unhandled write r%d offset %#08x\n",
>> +                dabt.reg, vpl011_reg);
>> +        return 0;
>> +    }
>> +
>> +write_ignore:
>> +    return 1;
>> +
>> +bad_width:
>> +    gprintk(XENLOG_ERR, "vpl011: bad write width %d r%d offset %#08x\n",
>> +            dabt.size, dabt.reg, vpl011_reg);
>> +    domain_crash_synchronous();
>> +    return 0;
>> +
>> +}
>> +
>> +static const struct mmio_handler_ops vpl011_mmio_handler = {
>> +    .read = vpl011_mmio_read,
>> +    .write = vpl011_mmio_write,
>> +};
>> +
>> +int vpl011_map_guest_page(struct domain *d, xen_pfn_t gfn)
>
>
> This function should either have the prototype defined in an header if used
> outside or static.
>
> Also, I have asked to use gfn_t and not xen_pfn_t. The former is a typesafe
> avoiding mix between MFN and GFN.
>
>> +{
>> +    struct vpl011_s *vpl011 = &d->arch.vpl011;
>> +
>> +    /* Map the guest PFN to Xen address space. */
>> +    return prepare_ring_for_helper(d,
>> +                                   gfn,
>> +                                   &vpl011->ring_page,
>> +                                   &vpl011->ring_buf);
>
>
> Looking at the usage it is only used in domain_vpl011_init. So I am not
> convinced of the of usefulness of this wrapper.
>
Yes. I will remove the wrapper function.

>> +}
>> +
>> +static void vpl011_data_avail(struct domain *d)
>> +{
>> +    unsigned long flags;
>> +    struct vpl011_s *vpl011 = &d->arch.vpl011;
>> +    struct xencons_interface *intf = vpl011->ring_buf;
>> +    RING_IDX in_ring_depth, out_ring_depth;
>
>
> I am a bit confused with the term "depth". It does not seem to fit for a
> ring.
I will use in_ring_qsize and out_ring_qsize instead.

>
>> +
>> +    VPL011_LOCK(d, flags);
>> +
>> +    in_ring_depth = vpl011_queued(intf->in_prod, intf->in_cons,
>> sizeof(intf->in));
>
>
> You introduced a macro VPL011_RING_DEPTH for hiding vpl011_queued, but don't
> use it.
I have removed this macro as much of ring buffer manipultation is
handled through vpl011_queued()
and vpl011_mask().

>
>> +    out_ring_depth = vpl011_queued(intf->out_prod, intf->out_cons,
>> sizeof(intf->out));
>
>
> Ditto.
>
>
>> +
>> +    /* Update the uart rx state if the buffer is not empty. */
>> +    if ( in_ring_depth != 0 )
>> +    {
>> +        vpl011->uartfr &= ~RXFE;
>> +        if ( in_ring_depth == VPL011_RING_MAX_DEPTH(intf, in) )
>> +            vpl011->uartfr |= RXFF;
>> +        vpl011->uartris |= RXI;
>> +    }
>> +
>> +    /* Update the uart tx state if the buffer is not full. */
>> +    if ( out_ring_depth != VPL011_RING_MAX_DEPTH(intf, out) )
>> +    {
>> +        vpl011->uartfr &= ~TXFF;
>> +        vpl011->uartris |= TXI;
>> +        if ( out_ring_depth == 0 )
>> +        {
>> +            vpl011->uartfr &= ~BUSY;
>> +            vpl011->uartfr |= TXFE;
>> +        }
>> +    }
>> +
>> +    VPL011_UNLOCK(d, flags);
>> +
>> +    vpl011_update_spi(d);
>> +}
>> +
>> +
>> +static void vpl011_notification(struct vcpu *v, unsigned int port)
>> +{
>> +    vpl011_data_avail(v->domain);
>> +}
>> +
>> +int domain_vpl011_init(struct domain *d,
>> +                       uint32_t console_domid,
>> +                       xen_pfn_t gfn,
>
>
> This should be gfn_t and not xen_pfn_t.
>
>> +                       evtchn_port_t *evtchn)
>> +{
>> +    int rc;
>> +    struct vpl011_s *vpl011 = &d->arch.vpl011;
>> +
>
>
> What if domain_vpl011_init is called twice? After nothing seem to prevent
> the new DOMCTL to be called twice.
>
I will add a check in the init function to return without doing
anything if vpl011 is already initialized.

>> +    rc = vpl011_map_guest_page(d, gfn);
>> +    if ( rc < 0 )
>> +        goto out;
>> +
>> +    rc = vgic_reserve_virq(d, GUEST_VPL011_SPI);
>> +    if ( !rc )
>> +    {
>> +        rc = -EINVAL;
>> +        goto out1;
>> +    }
>> +
>> +    register_mmio_handler(d, &vpl011_mmio_handler,
>> +                          GUEST_PL011_BASE, GUEST_PL011_SIZE, NULL);
>
>
> You register MMIO handler but never remove them. So if this call fail, you
> will end up with the handlers existing but the rest half-initialized which
> likely lead to a segfault, or worst leaking data.
>
I will free the mmio handlers in the vpl011 deinit function.

>> +
>> +    spin_lock_init(&vpl011->lock);
>> +
>> +    rc = alloc_unbound_xen_event_channel(d, 0, console_domid,
>> +                                         vpl011_notification);
>> +    if (rc < 0)
>
>
> Coding style:
>
> if (  ... )
>
ok.
>> +        goto out2;
>> +
>> +    vpl011->evtchn = *evtchn = rc;
>> +
>> +    vpl011->initialized = true;
>
>
> I am not sure to understand the purpose of this variable. You only use it in
> domain_vpl011_deinit. But you can easily know if you need to free by looking
> at the state of ring_buf.
It is an extraneous variable. I will remove it.

>
>> +
>> +    return 0;
>> +
>> +out2:
>> +    xfree(d->arch.vmmio.handlers);
>> +out1:
>
>
> I would have expected vgic_free_virq to be called in case of error.
>
ok.

>> +    destroy_ring_for_helper(&vpl011->ring_buf, vpl011->ring_page);
>> +out:
>> +    return rc;
>> +}
>> +
>> +void domain_vpl011_deinit(struct domain *d)
>> +{
>> +    struct vpl011_s *vpl011 = &d->arch.vpl011;
>> +
>> +    if ( vpl011->initialized )
>
>
> See my remark about vpl011->initialized above.
Yes, I will use the if ( vpl011->ring_buf ) check instead.
>
> In any case, I would prefer the condition to be inverted to avoid an extra
> layer of tab. I.e
>
> if ( !vpl011->initialized )
>    return;
>
ok.
>
>> +#ifndef _VPL011_H_
>> +
>> +#define _VPL011_H_
>> +
>> +#include <public/io/ring.h>
>> +#include <asm-arm/vreg.h>
>> +
>> +DEFINE_XEN_FLEX_RING(vpl011);
>
>
> I am sure someone already said it in a previous version. The vpl011 is the
> console ring. So why are we defining our own internally?
>
> At least this should have been used by xenconsole, but this is not the
> case... So we should really avoid defining our own ring here and re-use
> public/io/console.h.
>
>
>> +
>> +struct uartdr_reg {
>> +    uint8_t data;
>> +    uint8_t error_status:4;
>> +    uint8_t reserved1:4;
>> +    uint16_t reserved2;
>> +    uint32_t reserved3;
>> +};
>
>
> I don't see any benefits of this structure.
I will remove this structure and use the register value directly after
masking it.
>
>> +
>> +struct vpl011_s {
>
>
> Please remove the _s in the name.
ok.
>
>> +    void *ring_buf;
>> +    struct page_info *ring_page;
>> +    uint32_t    uartfr;     /* Flag register */
>> +    uint32_t    uartcr;     /* Control register */
>> +    uint32_t    uartimsc;   /* Interrupt mask register*/
>> +    uint32_t    uarticr;    /* Interrupt clear register */
>> +    uint32_t    uartris;    /* Raw interrupt status register */
>> +    uint32_t    uartmis;    /* Masked interrupt register */
>> +    spinlock_t  lock;
>> +    evtchn_port_t evtchn;
>> +    bool        initialized; /* Flag which tells whether vpl011 is
>> initialized */
>> +};
>> +
>> +#ifdef CONFIG_VPL011_CONSOLE
>> +int domain_vpl011_init(struct domain *d,
>> +                       uint32_t console_domid,
>> +                       xen_pfn_t gfn,
>> +                       evtchn_port_t *evtchn);
>> +void domain_vpl011_deinit(struct domain *d);
>> +#else
>> +int domain_vpl011_init(struct domain *d,
>
>
> static inline here.
ok.
>
>> +                       uint32_t console_domid,
>> +                       xen_pfn_t gfn,
>> +                       evtchn_port_t *evtchn) { return -ENOSYS; }
>
>
> Please indent correctly { return -ENOSYS };
>
ok.
>> +
>> +static inline void domain_vpl011_deinit(struct domain *d) { }
>
>
> Note that I am ok with the { } on the same line here.
>
>> diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h
>> index bd974fb..5f91207 100644
>> --- a/xen/include/public/arch-arm.h
>> +++ b/xen/include/public/arch-arm.h
>> @@ -322,6 +322,8 @@ struct xen_arch_domainconfig {
>>       *
>>       */
>>      uint32_t clock_frequency;
>> +
>> +    uint32_t console_domid;
>
>
> Why this is here?
I will move it out of here.

Regards,
Bhupinder

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.