1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) 2018 NXP
4  * (C) 2020 EPAM Systems Inc.
5  */
6 #include <common.h>
7 #include <cpu_func.h>
8 #include <dm.h>
9 #include <serial.h>
10 #include <watchdog.h>
11 #include <asm/global_data.h>
12 
13 #include <linux/bug.h>
14 
15 #include <xen/hvm.h>
16 #include <xen/events.h>
17 
18 #include <xen/interface/sched.h>
19 #include <xen/interface/hvm/hvm_op.h>
20 #include <xen/interface/hvm/params.h>
21 #include <xen/interface/io/console.h>
22 #include <xen/interface/io/ring.h>
23 
24 DECLARE_GLOBAL_DATA_PTR;
25 
26 u32 console_evtchn;
27 
28 /*
29  * struct xen_uart_priv - Structure representing a Xen UART info
30  * @intf:    Console I/O interface for Xen guest OSes
31  * @evtchn:  Console event channel
32  */
33 struct xen_uart_priv {
34 	struct xencons_interface *intf;
35 	u32 evtchn;
36 };
37 
xen_serial_setbrg(struct udevice * dev,int baudrate)38 int xen_serial_setbrg(struct udevice *dev, int baudrate)
39 {
40 	return 0;
41 }
42 
xen_serial_probe(struct udevice * dev)43 static int xen_serial_probe(struct udevice *dev)
44 {
45 	struct xen_uart_priv *priv = dev_get_priv(dev);
46 	u64 val = 0;
47 	unsigned long gfn;
48 	int ret;
49 
50 	ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &val);
51 	if (ret < 0 || val == 0)
52 		return ret;
53 
54 	priv->evtchn = val;
55 	console_evtchn = val;
56 
57 	ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &val);
58 	if (ret < 0)
59 		return ret;
60 
61 	if (!val)
62 		return -EINVAL;
63 
64 	gfn = val;
65 	priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
66 
67 	return 0;
68 }
69 
xen_serial_pending(struct udevice * dev,bool input)70 static int xen_serial_pending(struct udevice *dev, bool input)
71 {
72 	struct xen_uart_priv *priv = dev_get_priv(dev);
73 	struct xencons_interface *intf = priv->intf;
74 
75 	if (!input || intf->in_cons == intf->in_prod)
76 		return 0;
77 
78 	return 1;
79 }
80 
xen_serial_getc(struct udevice * dev)81 static int xen_serial_getc(struct udevice *dev)
82 {
83 	struct xen_uart_priv *priv = dev_get_priv(dev);
84 	struct xencons_interface *intf = priv->intf;
85 	XENCONS_RING_IDX cons;
86 	char c;
87 
88 	while (intf->in_cons == intf->in_prod)
89 		mb(); /* wait */
90 
91 	cons = intf->in_cons;
92 	mb();			/* get pointers before reading ring */
93 
94 	c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
95 
96 	mb();			/* read ring before consuming */
97 	intf->in_cons = cons;
98 
99 	notify_remote_via_evtchn(priv->evtchn);
100 
101 	return c;
102 }
103 
__write_console(struct udevice * dev,const char * data,int len)104 static int __write_console(struct udevice *dev, const char *data, int len)
105 {
106 	struct xen_uart_priv *priv = dev_get_priv(dev);
107 	struct xencons_interface *intf = priv->intf;
108 	XENCONS_RING_IDX cons, prod;
109 	int sent = 0;
110 
111 	cons = intf->out_cons;
112 	prod = intf->out_prod;
113 	mb(); /* Update pointer */
114 
115 	WARN_ON((prod - cons) > sizeof(intf->out));
116 
117 	while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
118 		intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
119 
120 	mb(); /* Update data before pointer */
121 	intf->out_prod = prod;
122 
123 	if (sent)
124 		notify_remote_via_evtchn(priv->evtchn);
125 
126 	return sent;
127 }
128 
write_console(struct udevice * dev,const char * data,int len)129 static int write_console(struct udevice *dev, const char *data, int len)
130 {
131 	/*
132 	 * Make sure the whole buffer is emitted, polling if
133 	 * necessary.  We don't ever want to rely on the hvc daemon
134 	 * because the most interesting console output is when the
135 	 * kernel is crippled.
136 	 */
137 	while (len) {
138 		int sent = __write_console(dev, data, len);
139 
140 		data += sent;
141 		len -= sent;
142 
143 		if (unlikely(len))
144 			HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
145 	}
146 
147 	return 0;
148 }
149 
xen_serial_putc(struct udevice * dev,const char ch)150 static int xen_serial_putc(struct udevice *dev, const char ch)
151 {
152 	write_console(dev, &ch, 1);
153 
154 	return 0;
155 }
156 
157 static const struct dm_serial_ops xen_serial_ops = {
158 	.putc = xen_serial_putc,
159 	.getc = xen_serial_getc,
160 	.pending = xen_serial_pending,
161 };
162 
163 #if CONFIG_IS_ENABLED(OF_CONTROL)
164 static const struct udevice_id xen_serial_ids[] = {
165 	{ .compatible = "xen,xen" },
166 	{ }
167 };
168 #endif
169 
170 U_BOOT_DRIVER(serial_xen) = {
171 	.name			= "serial_xen",
172 	.id			= UCLASS_SERIAL,
173 #if CONFIG_IS_ENABLED(OF_CONTROL)
174 	.of_match		= xen_serial_ids,
175 #endif
176 	.priv_auto	= sizeof(struct xen_uart_priv),
177 	.probe			= xen_serial_probe,
178 	.ops			= &xen_serial_ops,
179 #if !CONFIG_IS_ENABLED(OF_CONTROL)
180 	.flags			= DM_FLAG_PRE_RELOC,
181 #endif
182 };
183 
184