1
0
Fork 0

MLK-20373-5 serial: serial_xen: support normal uboot console

Support output/input using `xl console [domid]`.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Flynn xu <flynn.xu@nxp.com>
zero-sugar
Peng Fan 2018-11-15 13:18:53 +08:00
parent c96a9844cd
commit 75a9833506
1 changed files with 188 additions and 48 deletions

View File

@ -5,66 +5,206 @@
*/
#include <common.h>
#include <debug_uart.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <linux/compiler.h>
#include <serial.h>
extern void xenprintf(const char *buf);
extern void xenprintc(const char c);
#ifndef CONFIG_DM_SERIAL
#include <xen/events.h>
#include <xen/hvm.h>
#include <xen/interface/sched.h>
#include <xen/interface/hvm/hvm_op.h>
#include <xen/interface/hvm/params.h>
#include <xen/interface/io/console.h>
#include <xen/interface/io/ring.h>
static void xen_debug_serial_putc(const char c)
{
/* If \n, also do \r */
if (c == '\n')
serial_putc('\r');
DECLARE_GLOBAL_DATA_PTR;
xenprintc(c);
}
static void xen_debug_serial_puts(const char *buf)
{
xenprintf(buf);
}
static int xen_debug_serial_start(void)
{
return 0;
}
static void xen_debug_serial_setbrg(void)
{
}
static int xen_debug_serial_getc(void)
{
return 0;
}
static int xen_debug_serial_tstc(void)
{
return 0;
}
static struct serial_device xen_debug_serial_drv = {
.name = "xen_debug_serial",
.start = xen_debug_serial_start,
.stop = NULL,
.setbrg = xen_debug_serial_setbrg,
.putc = xen_debug_serial_putc,
.puts = xen_debug_serial_puts,
.getc = xen_debug_serial_getc,
.tstc = xen_debug_serial_tstc,
#ifdef CONFIG_DM_SERIAL
struct xen_uart_priv {
struct xencons_interface *intf;
u32 evtchn;
int vtermno;
struct hvc_struct *hvc;
grant_ref_t gntref;
};
void xen_debug_serial_initialize(void)
int xen_serial_setbrg(struct udevice *dev, int baudrate)
{
serial_register(&xen_debug_serial_drv);
return 0;
}
static int xen_serial_probe(struct udevice *dev)
{
struct xen_uart_priv *priv = dev_get_priv(dev);
u64 v = 0;
unsigned long gfn;
int r;
r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);
if (r < 0 || v == 0)
return r;
priv->evtchn = v;
r = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v);
if (r < 0 || v == 0)
return -ENODEV;
gfn = v;
priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
if (!priv->intf)
return -EINVAL;
return 0;
}
static int xen_serial_pending(struct udevice *dev, bool input)
{
struct xen_uart_priv *priv = dev_get_priv(dev);
struct xencons_interface *intf = priv->intf;
if (!input || intf->in_cons == intf->in_prod)
return 0;
return 1;
}
static int xen_serial_getc(struct udevice *dev)
{
struct xen_uart_priv *priv = dev_get_priv(dev);
struct xencons_interface *intf = priv->intf;
XENCONS_RING_IDX cons;
char c;
while (intf->in_cons == intf->in_prod) {
mb(); /* wait */
}
cons = intf->in_cons;
mb(); /* get pointers before reading ring */
c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
mb(); /* read ring before consuming */
intf->in_cons = cons;
notify_remote_via_evtchn(priv->evtchn);
return c;
}
static int __write_console(struct udevice *dev, const char *data, int len)
{
struct xen_uart_priv *priv = dev_get_priv(dev);
struct xencons_interface *intf = priv->intf;
XENCONS_RING_IDX cons, prod;
int sent = 0;
cons = intf->out_cons;
prod = intf->out_prod;
mb(); /* Update pointer */
WARN_ON((prod - cons) > sizeof(intf->out));
while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
mb(); /* Update data before pointer */
intf->out_prod = prod;
if (sent)
notify_remote_via_evtchn(priv->evtchn);
if (data[sent - 1] == '\n')
serial_puts("\r");
return sent;
}
static int write_console(struct udevice *dev, const char *data, int len)
{
/*
* Make sure the whole buffer is emitted, polling if
* necessary. We don't ever want to rely on the hvc daemon
* because the most interesting console output is when the
* kernel is crippled.
*/
while (len) {
int sent = __write_console(dev, data, len);
data += sent;
len -= sent;
if (unlikely(len))
HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
}
return 0;
}
static int xen_serial_puts(struct udevice *dev, const char *str)
{
write_console(dev, str, strlen(str));
return 0;
}
static int xen_serial_putc(struct udevice *dev, const char ch)
{
write_console(dev, &ch, 1);
return 0;
}
static const struct dm_serial_ops xen_serial_ops = {
.puts = xen_serial_puts,
.putc = xen_serial_putc,
.getc = xen_serial_getc,
.pending = xen_serial_pending,
};
static const struct udevice_id xen_serial_ids[] = {
{ .compatible = "xen,xen" },
{ }
};
U_BOOT_DRIVER(serial_xen) = {
.name = "serial_xen",
.id = UCLASS_SERIAL,
.of_match = xen_serial_ids,
.priv_auto_alloc_size = sizeof(struct xen_uart_priv),
.probe = xen_serial_probe,
.ops = &xen_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
};
#else
__weak struct serial_device *default_serial_console(void)
{
return &xen_debug_serial_drv;
return NULL;
}
#endif
#ifdef CONFIG_DEBUG_UART_XEN
void _debug_uart_init(void)
{
}
void _debug_uart_putc(int ch)
{
/* If \n, also do \r */
if (ch == '\n')
serial_putc('\r');
(void)HYPERVISOR_console_io(CONSOLEIO_write, 1, &ch);
return;
}
DEBUG_UART_FUNCS
#endif