[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH 03/14 v4] xen/arm: vpl011: Add pl011 uart emulation in Xen
Hi Andre, Thanks for your comments. >> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig >> index d46b98c..c1a0e7f 100644 >> --- a/xen/arch/arm/Kconfig >> +++ b/xen/arch/arm/Kconfig >> @@ -50,6 +50,11 @@ config HAS_ITS >> prompt "GICv3 ITS MSI controller support" if EXPERT = "y" >> depends on HAS_GICV3 >> >> +config VPL011_CONSOLE >> + bool "Emulated pl011 console support" >> + default y >> + ---help--- >> + Allows a guest to use pl011 UART as a console > > I admit that I am rather late with this comment, but I am not sure we > should advertise PL011 emulation here. > Technically what you emulate is a "SBSA Generic UART", which is indeed a > subset of the PL011, but really only a subset. You confirm this already > in patch 13/14, where you use the respective compatible name instead of > the generic arm,pl011 one. > > So while I don't dare to ask for renaming every identifier, I wonder if > we should at least keep the publicly visible part confined to "SBSA > UART". You could mention the subset nature in the help message here, for > instance. > The same reasoning applies to other parts of this series which introduce > user-visible strings (like in libxl). > >> endmenu >> I will rename the user visible part to "sbsa_uart". >> +#include <xen/errno.h> >> +#include <xen/event.h> >> +#include <xen/guest_access.h> >> +#include <xen/init.h> >> +#include <xen/lib.h> >> +#include <xen/mm.h> >> +#include <xen/sched.h> >> +#include <public/domctl.h> >> +#include <public/io/console.h> >> +#include <asm-arm/pl011-uart.h> >> +#include <asm-arm/vgic-emul.h> >> +#include <asm-arm/vpl011.h> > > I think Julien mentioned this already, but I think you should just use > "#include <asm/.. .h>" here. > ok. >> + >> +static bool vpl011_reg32_check_access(struct hsr_dabt dabt) >> +{ >> + return (dabt.size != DABT_DOUBLE_WORD); >> +} >> + >> +static void vpl011_update(struct domain *d) > > Can you please rename this function to indicate that it updates the > *interrupt status*? That name as it is rather generic at the moment. > I will rename it to vpl011_update_interrupt_status. >> +{ >> + struct vpl011 *vpl011 = &d->arch.vpl011; >> + >> + /* >> + * TODO: PL011 interrupts are level triggered which means >> + * that interrupt needs to be set/clear instead of being >> + * injected. However, currently vGIC does not handle level >> + * triggered interrupts properly. This function needs to be >> + * revisited once vGIC starts handling level triggered >> + * interrupts. >> + */ >> + if ( vpl011->uartris & vpl011->uartimsc ) >> + vgic_vcpu_inject_spi(d, GUEST_VPL011_SPI); > > So my understanding is that this is using an edge triggered semantic at > the moment. While this is not what an PL011 actually implements and not > what the driver really expects, this is fine on itself for now. > BUT I think we may want to avoid injecting spurious interrupts now: > If for instance the receive interrupt condition is set and we clear the > transmit interrupt bit, then call this function, it would inject a new > interrupt, although in a edge-triggered world we really wouldn't need to > do anything. > So I believe we would need to have some kind of shadowed interrupt > state, which stores the interrupt condition the guest already knows > about. As soon as we *add* a bit to this state, we inject the SPI. > This would act as a kind of temporary bridge between the level triggered > SBSA/PL011 UART and the edge-only VGIC implementation for now. > Later when the VGIC properly handles level triggered interrupts, this > implementation can be adjusted. But this change should then be confined > to this very function. > ok. I will update the logic accordingly. >> +} >> + >> +static uint8_t vpl011_read_data(struct domain *d) >> +{ >> + unsigned long flags; >> + uint8_t data = 0; >> + struct vpl011 *vpl011 = &d->arch.vpl011; >> + struct xencons_interface *intf = vpl011->ring_buf; >> + XENCONS_RING_IDX in_cons = intf->in_cons; >> + XENCONS_RING_IDX in_prod = intf->in_prod; >> + >> + VPL011_LOCK(d, flags); >> + >> + /* >> + * It is expected that there will be data in the ring buffer when this >> + * function is called since the guest is expected to read the data >> register >> + * only if the TXFE flag is not set. >> + * If the guest still does read when TXFE bit is set then 0 will be >> returned. >> + */ >> + if ( xencons_queued(in_prod, in_cons, sizeof(intf->in)) > 0 ) >> + { >> + data = intf->in[xencons_mask(in_cons, sizeof(intf->in))]; >> + in_cons += 1; >> + intf->in_cons = in_cons; >> + smp_mb(); >> + } >> + else >> + { >> + gprintk(XENLOG_ERR, "vpl011: Unexpected IN ring buffer empty\n"); >> + } >> + >> + if ( xencons_queued(in_prod, in_cons, sizeof(intf->in)) == 0 ) >> + { >> + vpl011->uartfr |= RXFE; >> + vpl011->uartris &= ~RXI; > > In a level triggered world you would need to possibly change the status > of the interrupt line here, so a call to (a renamed and fixed) > vpl011_update() function would be due here. To make the transition later > as easy as possible, I'd recommend to implement this shadow interrupt > state as described above and then call the interrupt update function > here already. ok. > >> + } >> + vpl011->uartfr &= ~RXFF; >> + VPL011_UNLOCK(d, flags); >> + >> + return data; >> +} >> + >> +static void vpl011_write_data(struct domain *d, uint8_t data) >> +{ >> + unsigned long flags; >> + struct vpl011 *vpl011 = &d->arch.vpl011; >> + struct xencons_interface *intf = vpl011->ring_buf; >> + XENCONS_RING_IDX out_cons = intf->out_cons; >> + XENCONS_RING_IDX out_prod = intf->out_prod; >> + >> + VPL011_LOCK(d, flags); >> + >> + /* >> + * It is expected that the ring is not full when this function is called >> + * as the guest is expected to write to the data register only when the >> + * TXFF flag is not set. >> + * In case the guest does write even when the TXFF flag is set then the >> + * data will be silently dropped. >> + */ >> + if ( xencons_queued(out_prod, out_cons, sizeof(intf->out)) != >> + sizeof (intf->out) ) >> + { >> + intf->out[xencons_mask(out_prod, sizeof(intf->out))] = data; >> + smp_wmb(); >> + out_prod += 1; >> + intf->out_prod = out_prod; >> + } >> + else >> + { >> + gprintk(XENLOG_ERR, "vpl011: Unexpected OUT ring buffer full\n"); >> + } >> + >> + if ( xencons_queued(out_prod, out_cons, sizeof(intf->out)) == >> + sizeof (intf->out) ) >> + { >> + vpl011->uartfr |= TXFF; >> + vpl011->uartris &= ~TXI; > > Same as above. Basically whenever you change one of the bits that may > affect the status of the interrupt line, I'd call the update function. ok. > >> + } >> + >> + vpl011->uartfr |= BUSY; >> + >> + vpl011->uartfr &= ~TXFE; >> + >> + VPL011_UNLOCK(d, flags); >> + >> + /* >> + * Send an event to console backend to indicate that there is >> + * data in the OUT ring buffer. >> + */ >> + notify_via_xen_event_channel(d, vpl011->evtchn); >> +} >> + >> +static int vpl011_mmio_read(struct vcpu *v, >> + mmio_info_t *info, >> + register_t *r, >> + void *priv) >> +{ >> + struct hsr_dabt dabt = info->dabt; >> + uint32_t vpl011_reg = (uint32_t)(info->gpa - GUEST_PL011_BASE); >> + struct vpl011 *vpl011 = &v->domain->arch.vpl011; >> + >> + switch ( vpl011_reg ) >> + { >> + case DR: >> + /* >> + * Since pl011 registers are 32-bit registers, all registers >> + * are handled similarly allowing 8-bit, 16-bit and 32-bit >> + * accesses. >> + */ >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + *r = vreg_reg32_extract(vpl011_read_data(v->domain), info); >> + return 1; >> + >> + case RSR: >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + /* It always returns 0 as there are no physical errors. */ >> + *r = 0; >> + return 1; >> + >> + case FR: >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + *r = vreg_reg32_extract(vpl011->uartfr, info); >> + return 1; >> + >> + case RIS: >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + *r = vreg_reg32_extract(vpl011->uartris, info); >> + return 1; >> + >> + case MIS: >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + *r = vreg_reg32_extract(vpl011->uartris & >> + vpl011->uartimsc, info); > > That smells like you need a lock here, since another access could change > either RIS and/or IMSC concurrently. > But as Julien already mentioned, these accesses are prone to races > anyway, since vreg_reg32_extract() is not atomic. > So is it worth to take the lock here just before the whole switch statement? > Yes, I have taken the lock while reading. >> + return 1; >> + >> + case IMSC: >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + *r = vreg_reg32_extract(vpl011->uartimsc, info); >> + return 1; >> + >> + case ICR: >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + /* Only write is valid. */ >> + return 0; >> + >> + default: >> + gprintk(XENLOG_ERR, "vpl011: unhandled read r%d offset %#08x\n", >> + dabt.reg, vpl011_reg); >> + return 0; >> + } >> + >> + return 1; >> + >> +bad_width: >> + gprintk(XENLOG_ERR, "vpl011: bad read width %d r%d offset %#08x\n", >> + dabt.size, dabt.reg, vpl011_reg); >> + domain_crash_synchronous(); >> + return 0; >> + >> +} >> + >> +static int vpl011_mmio_write(struct vcpu *v, >> + mmio_info_t *info, >> + register_t r, >> + void *priv) >> +{ >> + struct hsr_dabt dabt = info->dabt; >> + uint32_t vpl011_reg = (uint32_t)(info->gpa - GUEST_PL011_BASE); >> + struct vpl011 *vpl011 = &v->domain->arch.vpl011; >> + struct domain *d = v->domain; >> + unsigned long flags; >> + >> + switch ( vpl011_reg ) >> + { >> + case DR: >> + { >> + uint32_t data = 0; >> + >> + /* >> + * Since pl011 registers are 32-bit registers, all registers >> + * are handled similarly allowing 8-bit, 16-bit and 32-bit >> + * accesses. >> + */ >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + vreg_reg32_update(&data, r, info); >> + data &= 0xFF; > > This is not needed as vpl011_write_data()'s second argument is uint8_t, > so the compiler does this masking anyway. > And even if it wouldn't, you could move this statement into the call. > Yes. I will remove the masking. >> + vpl011_write_data(v->domain, data); >> + return 1; >> + } >> + case RSR: /* Nothing to clear. */ >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + return 1; >> + >> + case FR: >> + case RIS: >> + case MIS: >> + goto write_ignore; >> + >> + case IMSC: >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + VPL011_LOCK(d, flags); >> + vreg_reg32_update(&vpl011->uartimsc, r, info); >> + VPL011_UNLOCK(d, flags); >> + vpl011_update(v->domain); >> + return 1; >> + >> + case ICR: >> + if ( !vpl011_reg32_check_access(dabt) ) goto bad_width; >> + >> + VPL011_LOCK(d, flags); >> + vreg_reg32_clearbits(&vpl011->uartris, r, info); > > Please call the interrupt status update function here. > ok. >> + VPL011_UNLOCK(d, flags); >> + vpl011_update(d); >> + return 1; >> + >> + 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, >> +}; >> + >> +static void vpl011_data_avail(struct domain *d) >> +{ >> + unsigned long flags; >> + struct vpl011 *vpl011 = &d->arch.vpl011; >> + struct xencons_interface *intf = vpl011->ring_buf; >> + XENCONS_RING_IDX in_cons = intf->in_cons; >> + XENCONS_RING_IDX in_prod = intf->in_prod; >> + XENCONS_RING_IDX out_cons = intf->out_cons; >> + XENCONS_RING_IDX out_prod = intf->out_prod; >> + XENCONS_RING_IDX in_ring_qsize, out_ring_qsize; >> + >> + VPL011_LOCK(d, flags); >> + >> + in_ring_qsize = xencons_queued(in_prod, >> + in_cons, >> + sizeof(intf->in)); >> + >> + out_ring_qsize = xencons_queued(out_prod, >> + out_cons, >> + sizeof(intf->out)); >> + >> + /* Update the uart rx state if the buffer is not empty. */ >> + if ( in_ring_qsize != 0 ) >> + { >> + vpl011->uartfr &= ~RXFE; >> + if ( in_ring_qsize == sizeof(intf->in) ) >> + vpl011->uartfr |= RXFF; >> + vpl011->uartris |= RXI; > > ... and here ... > >> + } >> + >> + /* Update the uart tx state if the buffer is not full. */ >> + if ( out_ring_qsize != sizeof(intf->out) ) >> + { >> + vpl011->uartfr &= ~TXFF; >> + vpl011->uartris |= TXI; > > ... and here. > ok Regards, Bhupinder _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |