All of lore.kernel.org
 help / color / mirror / Atom feed
From: Bhupinder Thakur <bhupinder.thakur@linaro.org>
To: Julien Grall <julien.grall@arm.com>
Cc: xen-devel@lists.xenproject.org,
	Stefano Stabellini <sstabellini@kernel.org>
Subject: Re: [PATCH 03/12 v3] xen/arm: vpl011: Add pl011 uart emulation in Xen
Date: Fri, 26 May 2017 19:12:31 +0530	[thread overview]
Message-ID: <CACtJ1JS_zeCrort324k0TxGVQ6dyr3ceW94rycPeNazxSB18Vw@mail.gmail.com> (raw)
In-Reply-To: <ee9c0442-9d86-ac98-a30a-69c8ef1ed5de@arm.com>

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@lists.xen.org
https://lists.xen.org/xen-devel

  reply	other threads:[~2017-05-26 13:42 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-10 14:24 [PATCH 01/12 v3] xen/arm: vpl011: Move vgic register access functions to vreg.h Bhupinder Thakur
2017-05-10 14:24 ` [PATCH 02/12 v3] xen/arm: vpl011: Define generic vreg_reg* access functions in vreg.h Bhupinder Thakur
2017-05-22 12:21   ` Julien Grall
2017-05-23  6:14     ` Bhupinder Thakur
2017-05-10 14:24 ` [PATCH 03/12 v3] xen/arm: vpl011: Add pl011 uart emulation in Xen Bhupinder Thakur
2017-05-16 22:42   ` Stefano Stabellini
2017-05-22 14:24   ` Julien Grall
2017-05-26 13:42     ` Bhupinder Thakur [this message]
2017-05-29  7:13       ` Bhupinder Thakur
2017-05-29 18:26         ` Julien Grall
2017-06-01 13:34       ` Bhupinder Thakur
2017-06-01 13:56         ` Julien Grall
2017-06-01 10:33     ` Bhupinder Thakur
2017-06-01 12:42       ` Julien Grall
2017-05-22 12:18 ` [PATCH 01/12 v3] xen/arm: vpl011: Move vgic register access functions to vreg.h Julien Grall
2017-05-22 12:56   ` Bhupinder Thakur

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CACtJ1JS_zeCrort324k0TxGVQ6dyr3ceW94rycPeNazxSB18Vw@mail.gmail.com \
    --to=bhupinder.thakur@linaro.org \
    --cc=julien.grall@arm.com \
    --cc=sstabellini@kernel.org \
    --cc=xen-devel@lists.xenproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.