|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |