xref: /openbmc/linux/drivers/tty/hvc/hvc_xen.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2728674a7SGreg Kroah-Hartman /*
3728674a7SGreg Kroah-Hartman  * xen console driver interface to hvc_console.c
4728674a7SGreg Kroah-Hartman  *
5728674a7SGreg Kroah-Hartman  * (c) 2007 Gerd Hoffmann <kraxel@suse.de>
6728674a7SGreg Kroah-Hartman  */
7728674a7SGreg Kroah-Hartman 
8728674a7SGreg Kroah-Hartman #include <linux/console.h>
9728674a7SGreg Kroah-Hartman #include <linux/delay.h>
10728674a7SGreg Kroah-Hartman #include <linux/err.h>
114d9310e3SStefano Stabellini #include <linux/irq.h>
12728674a7SGreg Kroah-Hartman #include <linux/init.h>
13728674a7SGreg Kroah-Hartman #include <linux/types.h>
1402e19f9cSStefano Stabellini #include <linux/list.h>
1516e506efSStefano Stabellini #include <linux/serial_core.h>
16728674a7SGreg Kroah-Hartman 
17eb5ef071SStefano Stabellini #include <asm/io.h>
18728674a7SGreg Kroah-Hartman #include <asm/xen/hypervisor.h>
19728674a7SGreg Kroah-Hartman 
20728674a7SGreg Kroah-Hartman #include <xen/xen.h>
21eb5ef071SStefano Stabellini #include <xen/interface/xen.h>
22eb5ef071SStefano Stabellini #include <xen/hvm.h>
2302e19f9cSStefano Stabellini #include <xen/grant_table.h>
24728674a7SGreg Kroah-Hartman #include <xen/page.h>
25728674a7SGreg Kroah-Hartman #include <xen/events.h>
26728674a7SGreg Kroah-Hartman #include <xen/interface/io/console.h>
274d9310e3SStefano Stabellini #include <xen/interface/sched.h>
28728674a7SGreg Kroah-Hartman #include <xen/hvc-console.h>
2902e19f9cSStefano Stabellini #include <xen/xenbus.h>
30728674a7SGreg Kroah-Hartman 
31728674a7SGreg Kroah-Hartman #include "hvc_console.h"
32728674a7SGreg Kroah-Hartman 
33728674a7SGreg Kroah-Hartman #define HVC_COOKIE   0x58656e /* "Xen" in hex */
34728674a7SGreg Kroah-Hartman 
3502e19f9cSStefano Stabellini struct xencons_info {
3602e19f9cSStefano Stabellini 	struct list_head list;
3702e19f9cSStefano Stabellini 	struct xenbus_device *xbdev;
3802e19f9cSStefano Stabellini 	struct xencons_interface *intf;
3902e19f9cSStefano Stabellini 	unsigned int evtchn;
40fe415186SJuergen Gross 	XENCONS_RING_IDX out_cons;
41fe415186SJuergen Gross 	unsigned int out_cons_same;
4202e19f9cSStefano Stabellini 	struct hvc_struct *hvc;
4302e19f9cSStefano Stabellini 	int irq;
4402e19f9cSStefano Stabellini 	int vtermno;
4502e19f9cSStefano Stabellini 	grant_ref_t gntref;
466214894fSRoger Pau Monne 	spinlock_t ring_lock;
4702e19f9cSStefano Stabellini };
4802e19f9cSStefano Stabellini 
4902e19f9cSStefano Stabellini static LIST_HEAD(xenconsoles);
5002e19f9cSStefano Stabellini static DEFINE_SPINLOCK(xencons_lock);
51728674a7SGreg Kroah-Hartman 
52728674a7SGreg Kroah-Hartman /* ------------------------------------------------------------------ */
53728674a7SGreg Kroah-Hartman 
vtermno_to_xencons(int vtermno)5402e19f9cSStefano Stabellini static struct xencons_info *vtermno_to_xencons(int vtermno)
55728674a7SGreg Kroah-Hartman {
56c0dccad8SRoger Pau Monne 	struct xencons_info *entry, *ret = NULL;
57c0dccad8SRoger Pau Monne 	unsigned long flags;
5802e19f9cSStefano Stabellini 
59c0dccad8SRoger Pau Monne 	spin_lock_irqsave(&xencons_lock, flags);
60c0dccad8SRoger Pau Monne 	if (list_empty(&xenconsoles)) {
61c0dccad8SRoger Pau Monne 		spin_unlock_irqrestore(&xencons_lock, flags);
6202e19f9cSStefano Stabellini 		return NULL;
63c0dccad8SRoger Pau Monne 	}
6402e19f9cSStefano Stabellini 
65c0dccad8SRoger Pau Monne 	list_for_each_entry(entry, &xenconsoles, list) {
6602e19f9cSStefano Stabellini 		if (entry->vtermno == vtermno) {
6702e19f9cSStefano Stabellini 			ret  = entry;
6802e19f9cSStefano Stabellini 			break;
6902e19f9cSStefano Stabellini 		}
70728674a7SGreg Kroah-Hartman 	}
71c0dccad8SRoger Pau Monne 	spin_unlock_irqrestore(&xencons_lock, flags);
72728674a7SGreg Kroah-Hartman 
7302e19f9cSStefano Stabellini 	return ret;
7402e19f9cSStefano Stabellini }
7502e19f9cSStefano Stabellini 
xenbus_devid_to_vtermno(int devid)7602e19f9cSStefano Stabellini static inline int xenbus_devid_to_vtermno(int devid)
7702e19f9cSStefano Stabellini {
7802e19f9cSStefano Stabellini 	return devid + HVC_COOKIE;
7902e19f9cSStefano Stabellini }
8002e19f9cSStefano Stabellini 
notify_daemon(struct xencons_info * cons)8102e19f9cSStefano Stabellini static inline void notify_daemon(struct xencons_info *cons)
82728674a7SGreg Kroah-Hartman {
83728674a7SGreg Kroah-Hartman 	/* Use evtchn: this is called early, before irq is set up. */
8402e19f9cSStefano Stabellini 	notify_remote_via_evtchn(cons->evtchn);
85728674a7SGreg Kroah-Hartman }
86728674a7SGreg Kroah-Hartman 
__write_console(struct xencons_info * xencons,const char * data,int len)8702e19f9cSStefano Stabellini static int __write_console(struct xencons_info *xencons,
8802e19f9cSStefano Stabellini 		const char *data, int len)
89728674a7SGreg Kroah-Hartman {
90728674a7SGreg Kroah-Hartman 	XENCONS_RING_IDX cons, prod;
9102e19f9cSStefano Stabellini 	struct xencons_interface *intf = xencons->intf;
92728674a7SGreg Kroah-Hartman 	int sent = 0;
936214894fSRoger Pau Monne 	unsigned long flags;
94728674a7SGreg Kroah-Hartman 
956214894fSRoger Pau Monne 	spin_lock_irqsave(&xencons->ring_lock, flags);
96728674a7SGreg Kroah-Hartman 	cons = intf->out_cons;
97728674a7SGreg Kroah-Hartman 	prod = intf->out_prod;
98728674a7SGreg Kroah-Hartman 	mb();			/* update queue values before going on */
99e679004dSJuergen Gross 
100e679004dSJuergen Gross 	if ((prod - cons) > sizeof(intf->out)) {
1016214894fSRoger Pau Monne 		spin_unlock_irqrestore(&xencons->ring_lock, flags);
102e679004dSJuergen Gross 		pr_err_once("xencons: Illegal ring page indices");
103e679004dSJuergen Gross 		return -EINVAL;
104e679004dSJuergen Gross 	}
105728674a7SGreg Kroah-Hartman 
106728674a7SGreg Kroah-Hartman 	while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
107728674a7SGreg Kroah-Hartman 		intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
108728674a7SGreg Kroah-Hartman 
109728674a7SGreg Kroah-Hartman 	wmb();			/* write ring before updating pointer */
110728674a7SGreg Kroah-Hartman 	intf->out_prod = prod;
1116214894fSRoger Pau Monne 	spin_unlock_irqrestore(&xencons->ring_lock, flags);
112728674a7SGreg Kroah-Hartman 
113728674a7SGreg Kroah-Hartman 	if (sent)
11402e19f9cSStefano Stabellini 		notify_daemon(xencons);
115728674a7SGreg Kroah-Hartman 	return sent;
116728674a7SGreg Kroah-Hartman }
117728674a7SGreg Kroah-Hartman 
domU_write_console(uint32_t vtermno,const char * data,int len)118728674a7SGreg Kroah-Hartman static int domU_write_console(uint32_t vtermno, const char *data, int len)
119728674a7SGreg Kroah-Hartman {
120728674a7SGreg Kroah-Hartman 	int ret = len;
12102e19f9cSStefano Stabellini 	struct xencons_info *cons = vtermno_to_xencons(vtermno);
12202e19f9cSStefano Stabellini 	if (cons == NULL)
12302e19f9cSStefano Stabellini 		return -EINVAL;
124728674a7SGreg Kroah-Hartman 
125728674a7SGreg Kroah-Hartman 	/*
126728674a7SGreg Kroah-Hartman 	 * Make sure the whole buffer is emitted, polling if
127728674a7SGreg Kroah-Hartman 	 * necessary.  We don't ever want to rely on the hvc daemon
128728674a7SGreg Kroah-Hartman 	 * because the most interesting console output is when the
129728674a7SGreg Kroah-Hartman 	 * kernel is crippled.
130728674a7SGreg Kroah-Hartman 	 */
131728674a7SGreg Kroah-Hartman 	while (len) {
13202e19f9cSStefano Stabellini 		int sent = __write_console(cons, data, len);
133728674a7SGreg Kroah-Hartman 
134e679004dSJuergen Gross 		if (sent < 0)
135e679004dSJuergen Gross 			return sent;
136e679004dSJuergen Gross 
137728674a7SGreg Kroah-Hartman 		data += sent;
138728674a7SGreg Kroah-Hartman 		len -= sent;
139728674a7SGreg Kroah-Hartman 
140728674a7SGreg Kroah-Hartman 		if (unlikely(len))
141728674a7SGreg Kroah-Hartman 			HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
142728674a7SGreg Kroah-Hartman 	}
143728674a7SGreg Kroah-Hartman 
144728674a7SGreg Kroah-Hartman 	return ret;
145728674a7SGreg Kroah-Hartman }
146728674a7SGreg Kroah-Hartman 
domU_read_console(uint32_t vtermno,char * buf,int len)147728674a7SGreg Kroah-Hartman static int domU_read_console(uint32_t vtermno, char *buf, int len)
148728674a7SGreg Kroah-Hartman {
14902e19f9cSStefano Stabellini 	struct xencons_interface *intf;
150728674a7SGreg Kroah-Hartman 	XENCONS_RING_IDX cons, prod;
151728674a7SGreg Kroah-Hartman 	int recv = 0;
15202e19f9cSStefano Stabellini 	struct xencons_info *xencons = vtermno_to_xencons(vtermno);
153fe415186SJuergen Gross 	unsigned int eoiflag = 0;
1546214894fSRoger Pau Monne 	unsigned long flags;
155fe415186SJuergen Gross 
15602e19f9cSStefano Stabellini 	if (xencons == NULL)
15702e19f9cSStefano Stabellini 		return -EINVAL;
15802e19f9cSStefano Stabellini 	intf = xencons->intf;
159728674a7SGreg Kroah-Hartman 
1606214894fSRoger Pau Monne 	spin_lock_irqsave(&xencons->ring_lock, flags);
161728674a7SGreg Kroah-Hartman 	cons = intf->in_cons;
162728674a7SGreg Kroah-Hartman 	prod = intf->in_prod;
163728674a7SGreg Kroah-Hartman 	mb();			/* get pointers before reading ring */
164e679004dSJuergen Gross 
165e679004dSJuergen Gross 	if ((prod - cons) > sizeof(intf->in)) {
1666214894fSRoger Pau Monne 		spin_unlock_irqrestore(&xencons->ring_lock, flags);
167e679004dSJuergen Gross 		pr_err_once("xencons: Illegal ring page indices");
168e679004dSJuergen Gross 		return -EINVAL;
169e679004dSJuergen Gross 	}
170728674a7SGreg Kroah-Hartman 
171728674a7SGreg Kroah-Hartman 	while (cons != prod && recv < len)
172728674a7SGreg Kroah-Hartman 		buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
173728674a7SGreg Kroah-Hartman 
174728674a7SGreg Kroah-Hartman 	mb();			/* read ring before consuming */
175728674a7SGreg Kroah-Hartman 	intf->in_cons = cons;
176728674a7SGreg Kroah-Hartman 
177fe415186SJuergen Gross 	/*
178fe415186SJuergen Gross 	 * When to mark interrupt having been spurious:
179fe415186SJuergen Gross 	 * - there was no new data to be read, and
180fe415186SJuergen Gross 	 * - the backend did not consume some output bytes, and
181fe415186SJuergen Gross 	 * - the previous round with no read data didn't see consumed bytes
182fe415186SJuergen Gross 	 *   (we might have a race with an interrupt being in flight while
183fe415186SJuergen Gross 	 *   updating xencons->out_cons, so account for that by allowing one
184fe415186SJuergen Gross 	 *   round without any visible reason)
185fe415186SJuergen Gross 	 */
186fe415186SJuergen Gross 	if (intf->out_cons != xencons->out_cons) {
187fe415186SJuergen Gross 		xencons->out_cons = intf->out_cons;
188fe415186SJuergen Gross 		xencons->out_cons_same = 0;
189fe415186SJuergen Gross 	}
1906214894fSRoger Pau Monne 	if (!recv && xencons->out_cons_same++ > 1) {
1916214894fSRoger Pau Monne 		eoiflag = XEN_EOI_FLAG_SPURIOUS;
1926214894fSRoger Pau Monne 	}
1936214894fSRoger Pau Monne 	spin_unlock_irqrestore(&xencons->ring_lock, flags);
1946214894fSRoger Pau Monne 
195fe415186SJuergen Gross 	if (recv) {
19602e19f9cSStefano Stabellini 		notify_daemon(xencons);
197fe415186SJuergen Gross 	}
198fe415186SJuergen Gross 
199fe415186SJuergen Gross 	xen_irq_lateeoi(xencons->irq, eoiflag);
200fe415186SJuergen Gross 
201728674a7SGreg Kroah-Hartman 	return recv;
202728674a7SGreg Kroah-Hartman }
203728674a7SGreg Kroah-Hartman 
20454573c4aSJulia Lawall static const struct hv_ops domU_hvc_ops = {
205728674a7SGreg Kroah-Hartman 	.get_chars = domU_read_console,
206728674a7SGreg Kroah-Hartman 	.put_chars = domU_write_console,
207728674a7SGreg Kroah-Hartman 	.notifier_add = notifier_add_irq,
208728674a7SGreg Kroah-Hartman 	.notifier_del = notifier_del_irq,
209728674a7SGreg Kroah-Hartman 	.notifier_hangup = notifier_hangup_irq,
210728674a7SGreg Kroah-Hartman };
211728674a7SGreg Kroah-Hartman 
dom0_read_console(uint32_t vtermno,char * buf,int len)212728674a7SGreg Kroah-Hartman static int dom0_read_console(uint32_t vtermno, char *buf, int len)
213728674a7SGreg Kroah-Hartman {
214728674a7SGreg Kroah-Hartman 	return HYPERVISOR_console_io(CONSOLEIO_read, len, buf);
215728674a7SGreg Kroah-Hartman }
216728674a7SGreg Kroah-Hartman 
217728674a7SGreg Kroah-Hartman /*
218728674a7SGreg Kroah-Hartman  * Either for a dom0 to write to the system console, or a domU with a
219728674a7SGreg Kroah-Hartman  * debug version of Xen
220728674a7SGreg Kroah-Hartman  */
dom0_write_console(uint32_t vtermno,const char * str,int len)221728674a7SGreg Kroah-Hartman static int dom0_write_console(uint32_t vtermno, const char *str, int len)
222728674a7SGreg Kroah-Hartman {
223728674a7SGreg Kroah-Hartman 	int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str);
224728674a7SGreg Kroah-Hartman 	if (rc < 0)
22504b772d2SKonrad Rzeszutek Wilk 		return rc;
226728674a7SGreg Kroah-Hartman 
227728674a7SGreg Kroah-Hartman 	return len;
228728674a7SGreg Kroah-Hartman }
229728674a7SGreg Kroah-Hartman 
23054573c4aSJulia Lawall static const struct hv_ops dom0_hvc_ops = {
231728674a7SGreg Kroah-Hartman 	.get_chars = dom0_read_console,
232728674a7SGreg Kroah-Hartman 	.put_chars = dom0_write_console,
233728674a7SGreg Kroah-Hartman 	.notifier_add = notifier_add_irq,
234728674a7SGreg Kroah-Hartman 	.notifier_del = notifier_del_irq,
235728674a7SGreg Kroah-Hartman 	.notifier_hangup = notifier_hangup_irq,
236728674a7SGreg Kroah-Hartman };
237728674a7SGreg Kroah-Hartman 
xen_hvm_console_init(void)238eb5ef071SStefano Stabellini static int xen_hvm_console_init(void)
239eb5ef071SStefano Stabellini {
240eb5ef071SStefano Stabellini 	int r;
241eb5ef071SStefano Stabellini 	uint64_t v = 0;
242c0dccad8SRoger Pau Monne 	unsigned long gfn, flags;
24302e19f9cSStefano Stabellini 	struct xencons_info *info;
244eb5ef071SStefano Stabellini 
245eb5ef071SStefano Stabellini 	if (!xen_hvm_domain())
246eb5ef071SStefano Stabellini 		return -ENODEV;
247eb5ef071SStefano Stabellini 
24802e19f9cSStefano Stabellini 	info = vtermno_to_xencons(HVC_COOKIE);
24902e19f9cSStefano Stabellini 	if (!info) {
2502d1d3f3aSJoe Perches 		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
25102e19f9cSStefano Stabellini 		if (!info)
25202e19f9cSStefano Stabellini 			return -ENOMEM;
2536214894fSRoger Pau Monne 		spin_lock_init(&info->ring_lock);
25437a80bf5SKonrad Rzeszutek Wilk 	} else if (info->intf != NULL) {
25502e19f9cSStefano Stabellini 		/* already configured */
25602e19f9cSStefano Stabellini 		return 0;
25737a80bf5SKonrad Rzeszutek Wilk 	}
2585842f576SKonrad Rzeszutek Wilk 	/*
2595842f576SKonrad Rzeszutek Wilk 	 * If the toolstack (or the hypervisor) hasn't set these values, the
260859e3267SJulien Grall 	 * default value is 0. Even though gfn = 0 and evtchn = 0 are
2615842f576SKonrad Rzeszutek Wilk 	 * theoretically correct values, in practice they never are and they
2625842f576SKonrad Rzeszutek Wilk 	 * mean that a legacy toolstack hasn't initialized the pv console correctly.
2635842f576SKonrad Rzeszutek Wilk 	 */
264eb5ef071SStefano Stabellini 	r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);
2655842f576SKonrad Rzeszutek Wilk 	if (r < 0 || v == 0)
2662e5ad6b9SKonrad Rzeszutek Wilk 		goto err;
26702e19f9cSStefano Stabellini 	info->evtchn = v;
268a32c88b9SKonrad Rzeszutek Wilk 	v = 0;
269a32c88b9SKonrad Rzeszutek Wilk 	r = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v);
2705842f576SKonrad Rzeszutek Wilk 	if (r < 0 || v == 0)
2712e5ad6b9SKonrad Rzeszutek Wilk 		goto err;
272859e3267SJulien Grall 	gfn = v;
27341925b10SJuergen Gross 	info->intf = memremap(gfn << XEN_PAGE_SHIFT, XEN_PAGE_SIZE, MEMREMAP_WB);
2742e5ad6b9SKonrad Rzeszutek Wilk 	if (info->intf == NULL)
2752e5ad6b9SKonrad Rzeszutek Wilk 		goto err;
27602e19f9cSStefano Stabellini 	info->vtermno = HVC_COOKIE;
27702e19f9cSStefano Stabellini 
278c0dccad8SRoger Pau Monne 	spin_lock_irqsave(&xencons_lock, flags);
27902e19f9cSStefano Stabellini 	list_add_tail(&info->list, &xenconsoles);
280c0dccad8SRoger Pau Monne 	spin_unlock_irqrestore(&xencons_lock, flags);
28102e19f9cSStefano Stabellini 
28202e19f9cSStefano Stabellini 	return 0;
2832e5ad6b9SKonrad Rzeszutek Wilk err:
2842e5ad6b9SKonrad Rzeszutek Wilk 	kfree(info);
2852e5ad6b9SKonrad Rzeszutek Wilk 	return -ENODEV;
28602e19f9cSStefano Stabellini }
28702e19f9cSStefano Stabellini 
xencons_info_pv_init(struct xencons_info * info,int vtermno)2885de738b3SStefano Stabellini static int xencons_info_pv_init(struct xencons_info *info, int vtermno)
2895de738b3SStefano Stabellini {
2906214894fSRoger Pau Monne 	spin_lock_init(&info->ring_lock);
2915de738b3SStefano Stabellini 	info->evtchn = xen_start_info->console.domU.evtchn;
2925de738b3SStefano Stabellini 	/* GFN == MFN for PV guest */
2935de738b3SStefano Stabellini 	info->intf = gfn_to_virt(xen_start_info->console.domU.mfn);
2945de738b3SStefano Stabellini 	info->vtermno = vtermno;
2955de738b3SStefano Stabellini 
2965de738b3SStefano Stabellini 	list_add_tail(&info->list, &xenconsoles);
2975de738b3SStefano Stabellini 
2985de738b3SStefano Stabellini 	return 0;
2995de738b3SStefano Stabellini }
3005de738b3SStefano Stabellini 
xen_pv_console_init(void)30102e19f9cSStefano Stabellini static int xen_pv_console_init(void)
30202e19f9cSStefano Stabellini {
30302e19f9cSStefano Stabellini 	struct xencons_info *info;
304c0dccad8SRoger Pau Monne 	unsigned long flags;
30502e19f9cSStefano Stabellini 
30602e19f9cSStefano Stabellini 	if (!xen_pv_domain())
30702e19f9cSStefano Stabellini 		return -ENODEV;
30802e19f9cSStefano Stabellini 
30902e19f9cSStefano Stabellini 	if (!xen_start_info->console.domU.evtchn)
31002e19f9cSStefano Stabellini 		return -ENODEV;
31102e19f9cSStefano Stabellini 
31202e19f9cSStefano Stabellini 	info = vtermno_to_xencons(HVC_COOKIE);
31302e19f9cSStefano Stabellini 	if (!info) {
3142d1d3f3aSJoe Perches 		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
31502e19f9cSStefano Stabellini 		if (!info)
31602e19f9cSStefano Stabellini 			return -ENOMEM;
31737a80bf5SKonrad Rzeszutek Wilk 	} else if (info->intf != NULL) {
31802e19f9cSStefano Stabellini 		/* already configured */
31902e19f9cSStefano Stabellini 		return 0;
32037a80bf5SKonrad Rzeszutek Wilk 	}
321c0dccad8SRoger Pau Monne 	spin_lock_irqsave(&xencons_lock, flags);
3225de738b3SStefano Stabellini 	xencons_info_pv_init(info, HVC_COOKIE);
323c0dccad8SRoger Pau Monne 	spin_unlock_irqrestore(&xencons_lock, flags);
32402e19f9cSStefano Stabellini 
32502e19f9cSStefano Stabellini 	return 0;
32602e19f9cSStefano Stabellini }
32702e19f9cSStefano Stabellini 
xen_initial_domain_console_init(void)32802e19f9cSStefano Stabellini static int xen_initial_domain_console_init(void)
32902e19f9cSStefano Stabellini {
33002e19f9cSStefano Stabellini 	struct xencons_info *info;
331c0dccad8SRoger Pau Monne 	unsigned long flags;
33202e19f9cSStefano Stabellini 
33302e19f9cSStefano Stabellini 	if (!xen_initial_domain())
33402e19f9cSStefano Stabellini 		return -ENODEV;
33502e19f9cSStefano Stabellini 
33602e19f9cSStefano Stabellini 	info = vtermno_to_xencons(HVC_COOKIE);
33702e19f9cSStefano Stabellini 	if (!info) {
3382d1d3f3aSJoe Perches 		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
33902e19f9cSStefano Stabellini 		if (!info)
34002e19f9cSStefano Stabellini 			return -ENOMEM;
3416214894fSRoger Pau Monne 		spin_lock_init(&info->ring_lock);
34202e19f9cSStefano Stabellini 	}
34302e19f9cSStefano Stabellini 
34477bb3dfdSDavid Vrabel 	info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);
34502e19f9cSStefano Stabellini 	info->vtermno = HVC_COOKIE;
34602e19f9cSStefano Stabellini 
347c0dccad8SRoger Pau Monne 	spin_lock_irqsave(&xencons_lock, flags);
34802e19f9cSStefano Stabellini 	list_add_tail(&info->list, &xenconsoles);
349c0dccad8SRoger Pau Monne 	spin_unlock_irqrestore(&xencons_lock, flags);
350eb5ef071SStefano Stabellini 
351eb5ef071SStefano Stabellini 	return 0;
352eb5ef071SStefano Stabellini }
353eb5ef071SStefano Stabellini 
xen_console_update_evtchn(struct xencons_info * info)354b9d934f2SBoris Ostrovsky static void xen_console_update_evtchn(struct xencons_info *info)
355b9d934f2SBoris Ostrovsky {
356b9d934f2SBoris Ostrovsky 	if (xen_hvm_domain()) {
357c4ace5daSJan Beulich 		uint64_t v = 0;
358b9d934f2SBoris Ostrovsky 		int err;
359b9d934f2SBoris Ostrovsky 
360b9d934f2SBoris Ostrovsky 		err = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);
361b9d934f2SBoris Ostrovsky 		if (!err && v)
362b9d934f2SBoris Ostrovsky 			info->evtchn = v;
363b9d934f2SBoris Ostrovsky 	} else
364b9d934f2SBoris Ostrovsky 		info->evtchn = xen_start_info->console.domU.evtchn;
365b9d934f2SBoris Ostrovsky }
366b9d934f2SBoris Ostrovsky 
xen_console_resume(void)367728674a7SGreg Kroah-Hartman void xen_console_resume(void)
368728674a7SGreg Kroah-Hartman {
36902e19f9cSStefano Stabellini 	struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE);
370b9d934f2SBoris Ostrovsky 	if (info != NULL && info->irq) {
371b9d934f2SBoris Ostrovsky 		if (!xen_initial_domain())
372b9d934f2SBoris Ostrovsky 			xen_console_update_evtchn(info);
37302e19f9cSStefano Stabellini 		rebind_evtchn_irq(info->evtchn, info->irq);
374728674a7SGreg Kroah-Hartman 	}
375b9d934f2SBoris Ostrovsky }
376728674a7SGreg Kroah-Hartman 
377e36ae343SArnd Bergmann #ifdef CONFIG_HVC_XEN_FRONTEND
xencons_disconnect_backend(struct xencons_info * info)37802e19f9cSStefano Stabellini static void xencons_disconnect_backend(struct xencons_info *info)
37902e19f9cSStefano Stabellini {
38011e5dac7SDavid Woodhouse 	if (info->hvc != NULL)
38111e5dac7SDavid Woodhouse 		hvc_remove(info->hvc);
38211e5dac7SDavid Woodhouse 	info->hvc = NULL;
38311e5dac7SDavid Woodhouse 	if (info->irq > 0) {
38411e5dac7SDavid Woodhouse 		evtchn_put(info->evtchn);
38502e19f9cSStefano Stabellini 		info->irq = 0;
38611e5dac7SDavid Woodhouse 		info->evtchn = 0;
38711e5dac7SDavid Woodhouse 	}
38811e5dac7SDavid Woodhouse 	/* evtchn_put() will also close it so this is only an error path */
38902e19f9cSStefano Stabellini 	if (info->evtchn > 0)
39002e19f9cSStefano Stabellini 		xenbus_free_evtchn(info->xbdev, info->evtchn);
39102e19f9cSStefano Stabellini 	info->evtchn = 0;
39202e19f9cSStefano Stabellini 	if (info->gntref > 0)
39302e19f9cSStefano Stabellini 		gnttab_free_grant_references(info->gntref);
39402e19f9cSStefano Stabellini 	info->gntref = 0;
39502e19f9cSStefano Stabellini }
39602e19f9cSStefano Stabellini 
xencons_free(struct xencons_info * info)39702e19f9cSStefano Stabellini static void xencons_free(struct xencons_info *info)
39802e19f9cSStefano Stabellini {
39902e19f9cSStefano Stabellini 	free_page((unsigned long)info->intf);
40002e19f9cSStefano Stabellini 	info->intf = NULL;
40102e19f9cSStefano Stabellini 	info->vtermno = 0;
40202e19f9cSStefano Stabellini 	kfree(info);
40302e19f9cSStefano Stabellini }
40402e19f9cSStefano Stabellini 
xen_console_remove(struct xencons_info * info)40502e19f9cSStefano Stabellini static int xen_console_remove(struct xencons_info *info)
40602e19f9cSStefano Stabellini {
407c0dccad8SRoger Pau Monne 	unsigned long flags;
408c0dccad8SRoger Pau Monne 
40902e19f9cSStefano Stabellini 	xencons_disconnect_backend(info);
410c0dccad8SRoger Pau Monne 	spin_lock_irqsave(&xencons_lock, flags);
41102e19f9cSStefano Stabellini 	list_del(&info->list);
412c0dccad8SRoger Pau Monne 	spin_unlock_irqrestore(&xencons_lock, flags);
41302e19f9cSStefano Stabellini 	if (info->xbdev != NULL)
41402e19f9cSStefano Stabellini 		xencons_free(info);
41502e19f9cSStefano Stabellini 	else {
41602e19f9cSStefano Stabellini 		if (xen_hvm_domain())
41702e19f9cSStefano Stabellini 			iounmap(info->intf);
41802e19f9cSStefano Stabellini 		kfree(info);
41902e19f9cSStefano Stabellini 	}
42002e19f9cSStefano Stabellini 	return 0;
42102e19f9cSStefano Stabellini }
42202e19f9cSStefano Stabellini 
xencons_remove(struct xenbus_device * dev)4237cffcadeSDawei Li static void xencons_remove(struct xenbus_device *dev)
42402e19f9cSStefano Stabellini {
4257cffcadeSDawei Li 	xen_console_remove(dev_get_drvdata(&dev->dev));
42602e19f9cSStefano Stabellini }
42702e19f9cSStefano Stabellini 
xencons_connect_backend(struct xenbus_device * dev,struct xencons_info * info)42802e19f9cSStefano Stabellini static int xencons_connect_backend(struct xenbus_device *dev,
42902e19f9cSStefano Stabellini 				  struct xencons_info *info)
43002e19f9cSStefano Stabellini {
43102e19f9cSStefano Stabellini 	int ret, evtchn, devid, ref, irq;
43202e19f9cSStefano Stabellini 	struct xenbus_transaction xbt;
43302e19f9cSStefano Stabellini 	grant_ref_t gref_head;
43402e19f9cSStefano Stabellini 
43502e19f9cSStefano Stabellini 	ret = xenbus_alloc_evtchn(dev, &evtchn);
43602e19f9cSStefano Stabellini 	if (ret)
43702e19f9cSStefano Stabellini 		return ret;
43802e19f9cSStefano Stabellini 	info->evtchn = evtchn;
439*192e6ebaSDavid Woodhouse 	irq = bind_evtchn_to_irq_lateeoi(evtchn);
44002e19f9cSStefano Stabellini 	if (irq < 0)
44102e19f9cSStefano Stabellini 		return irq;
44202e19f9cSStefano Stabellini 	info->irq = irq;
44302e19f9cSStefano Stabellini 	devid = dev->nodename[strlen(dev->nodename) - 1] - '0';
44402e19f9cSStefano Stabellini 	info->hvc = hvc_alloc(xenbus_devid_to_vtermno(devid),
44502e19f9cSStefano Stabellini 			irq, &domU_hvc_ops, 256);
44602e19f9cSStefano Stabellini 	if (IS_ERR(info->hvc))
44702e19f9cSStefano Stabellini 		return PTR_ERR(info->hvc);
44802e19f9cSStefano Stabellini 	ret = gnttab_alloc_grant_references(1, &gref_head);
44902e19f9cSStefano Stabellini 	if (ret < 0)
45002e19f9cSStefano Stabellini 		return ret;
45102e19f9cSStefano Stabellini 	info->gntref = gref_head;
45202e19f9cSStefano Stabellini 	ref = gnttab_claim_grant_reference(&gref_head);
45302e19f9cSStefano Stabellini 	if (ref < 0)
45402e19f9cSStefano Stabellini 		return ref;
45502e19f9cSStefano Stabellini 	gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id,
456859e3267SJulien Grall 					virt_to_gfn(info->intf), 0);
45702e19f9cSStefano Stabellini 
45802e19f9cSStefano Stabellini  again:
45902e19f9cSStefano Stabellini 	ret = xenbus_transaction_start(&xbt);
46002e19f9cSStefano Stabellini 	if (ret) {
46102e19f9cSStefano Stabellini 		xenbus_dev_fatal(dev, ret, "starting transaction");
46202e19f9cSStefano Stabellini 		return ret;
46302e19f9cSStefano Stabellini 	}
46402e19f9cSStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "ring-ref", "%d", ref);
46502e19f9cSStefano Stabellini 	if (ret)
46602e19f9cSStefano Stabellini 		goto error_xenbus;
46702e19f9cSStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "port", "%u",
46802e19f9cSStefano Stabellini 			    evtchn);
46902e19f9cSStefano Stabellini 	if (ret)
47002e19f9cSStefano Stabellini 		goto error_xenbus;
47102e19f9cSStefano Stabellini 	ret = xenbus_transaction_end(xbt, 0);
47202e19f9cSStefano Stabellini 	if (ret) {
47302e19f9cSStefano Stabellini 		if (ret == -EAGAIN)
47402e19f9cSStefano Stabellini 			goto again;
47502e19f9cSStefano Stabellini 		xenbus_dev_fatal(dev, ret, "completing transaction");
47602e19f9cSStefano Stabellini 		return ret;
47702e19f9cSStefano Stabellini 	}
47802e19f9cSStefano Stabellini 
47902e19f9cSStefano Stabellini 	xenbus_switch_state(dev, XenbusStateInitialised);
48002e19f9cSStefano Stabellini 	return 0;
48102e19f9cSStefano Stabellini 
48202e19f9cSStefano Stabellini  error_xenbus:
48302e19f9cSStefano Stabellini 	xenbus_transaction_end(xbt, 1);
48402e19f9cSStefano Stabellini 	xenbus_dev_fatal(dev, ret, "writing xenstore");
48502e19f9cSStefano Stabellini 	return ret;
48602e19f9cSStefano Stabellini }
48702e19f9cSStefano Stabellini 
xencons_probe(struct xenbus_device * dev,const struct xenbus_device_id * id)4889671f099SBill Pemberton static int xencons_probe(struct xenbus_device *dev,
48902e19f9cSStefano Stabellini 				  const struct xenbus_device_id *id)
49002e19f9cSStefano Stabellini {
49102e19f9cSStefano Stabellini 	int ret, devid;
49202e19f9cSStefano Stabellini 	struct xencons_info *info;
493c0dccad8SRoger Pau Monne 	unsigned long flags;
49402e19f9cSStefano Stabellini 
49502e19f9cSStefano Stabellini 	devid = dev->nodename[strlen(dev->nodename) - 1] - '0';
49602e19f9cSStefano Stabellini 	if (devid == 0)
49702e19f9cSStefano Stabellini 		return -ENODEV;
49802e19f9cSStefano Stabellini 
499201a52beSDan Carpenter 	info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);
50002e19f9cSStefano Stabellini 	if (!info)
501201a52beSDan Carpenter 		return -ENOMEM;
5026214894fSRoger Pau Monne 	spin_lock_init(&info->ring_lock);
50302e19f9cSStefano Stabellini 	dev_set_drvdata(&dev->dev, info);
50402e19f9cSStefano Stabellini 	info->xbdev = dev;
50502e19f9cSStefano Stabellini 	info->vtermno = xenbus_devid_to_vtermno(devid);
50602e19f9cSStefano Stabellini 	info->intf = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
50702e19f9cSStefano Stabellini 	if (!info->intf)
50802e19f9cSStefano Stabellini 		goto error_nomem;
50902e19f9cSStefano Stabellini 
51002e19f9cSStefano Stabellini 	ret = xencons_connect_backend(dev, info);
51102e19f9cSStefano Stabellini 	if (ret < 0)
51202e19f9cSStefano Stabellini 		goto error;
513c0dccad8SRoger Pau Monne 	spin_lock_irqsave(&xencons_lock, flags);
51402e19f9cSStefano Stabellini 	list_add_tail(&info->list, &xenconsoles);
515c0dccad8SRoger Pau Monne 	spin_unlock_irqrestore(&xencons_lock, flags);
51602e19f9cSStefano Stabellini 
51702e19f9cSStefano Stabellini 	return 0;
51802e19f9cSStefano Stabellini 
51902e19f9cSStefano Stabellini  error_nomem:
52002e19f9cSStefano Stabellini 	ret = -ENOMEM;
52102e19f9cSStefano Stabellini 	xenbus_dev_fatal(dev, ret, "allocating device memory");
52202e19f9cSStefano Stabellini  error:
52302e19f9cSStefano Stabellini 	xencons_disconnect_backend(info);
52402e19f9cSStefano Stabellini 	xencons_free(info);
52502e19f9cSStefano Stabellini 	return ret;
52602e19f9cSStefano Stabellini }
52702e19f9cSStefano Stabellini 
xencons_resume(struct xenbus_device * dev)52802e19f9cSStefano Stabellini static int xencons_resume(struct xenbus_device *dev)
52902e19f9cSStefano Stabellini {
53002e19f9cSStefano Stabellini 	struct xencons_info *info = dev_get_drvdata(&dev->dev);
53102e19f9cSStefano Stabellini 
53202e19f9cSStefano Stabellini 	xencons_disconnect_backend(info);
5339652c080SJulien Grall 	memset(info->intf, 0, XEN_PAGE_SIZE);
53402e19f9cSStefano Stabellini 	return xencons_connect_backend(dev, info);
53502e19f9cSStefano Stabellini }
53602e19f9cSStefano Stabellini 
xencons_backend_changed(struct xenbus_device * dev,enum xenbus_state backend_state)53702e19f9cSStefano Stabellini static void xencons_backend_changed(struct xenbus_device *dev,
53802e19f9cSStefano Stabellini 				   enum xenbus_state backend_state)
53902e19f9cSStefano Stabellini {
54002e19f9cSStefano Stabellini 	switch (backend_state) {
54102e19f9cSStefano Stabellini 	case XenbusStateReconfiguring:
54202e19f9cSStefano Stabellini 	case XenbusStateReconfigured:
54302e19f9cSStefano Stabellini 	case XenbusStateInitialising:
54402e19f9cSStefano Stabellini 	case XenbusStateInitialised:
54502e19f9cSStefano Stabellini 	case XenbusStateUnknown:
54602e19f9cSStefano Stabellini 		break;
54702e19f9cSStefano Stabellini 
54802e19f9cSStefano Stabellini 	case XenbusStateInitWait:
54902e19f9cSStefano Stabellini 		break;
55002e19f9cSStefano Stabellini 
55102e19f9cSStefano Stabellini 	case XenbusStateConnected:
55202e19f9cSStefano Stabellini 		xenbus_switch_state(dev, XenbusStateConnected);
55302e19f9cSStefano Stabellini 		break;
55402e19f9cSStefano Stabellini 
5559b6934a3SDavid Vrabel 	case XenbusStateClosed:
5569b6934a3SDavid Vrabel 		if (dev->state == XenbusStateClosed)
5579b6934a3SDavid Vrabel 			break;
558df561f66SGustavo A. R. Silva 		fallthrough;	/* Missed the backend's CLOSING state */
55911e5dac7SDavid Woodhouse 	case XenbusStateClosing: {
56011e5dac7SDavid Woodhouse 		struct xencons_info *info = dev_get_drvdata(&dev->dev);;
56111e5dac7SDavid Woodhouse 
56211e5dac7SDavid Woodhouse 		/*
56311e5dac7SDavid Woodhouse 		 * Don't tear down the evtchn and grant ref before the other
56411e5dac7SDavid Woodhouse 		 * end has disconnected, but do stop userspace from trying
56511e5dac7SDavid Woodhouse 		 * to use the device before we allow the backend to close.
56611e5dac7SDavid Woodhouse 		 */
56711e5dac7SDavid Woodhouse 		if (info->hvc) {
56811e5dac7SDavid Woodhouse 			hvc_remove(info->hvc);
56911e5dac7SDavid Woodhouse 			info->hvc = NULL;
57011e5dac7SDavid Woodhouse 		}
57111e5dac7SDavid Woodhouse 
57202e19f9cSStefano Stabellini 		xenbus_frontend_closed(dev);
57302e19f9cSStefano Stabellini 		break;
57402e19f9cSStefano Stabellini 	}
57502e19f9cSStefano Stabellini 	}
57611e5dac7SDavid Woodhouse }
57702e19f9cSStefano Stabellini 
57802e19f9cSStefano Stabellini static const struct xenbus_device_id xencons_ids[] = {
57902e19f9cSStefano Stabellini 	{ "console" },
58002e19f9cSStefano Stabellini 	{ "" }
58102e19f9cSStefano Stabellini };
58202e19f9cSStefano Stabellini 
58395afae48SDavid Vrabel static struct xenbus_driver xencons_driver = {
58495afae48SDavid Vrabel 	.name = "xenconsole",
58595afae48SDavid Vrabel 	.ids = xencons_ids,
586cf8e019bSStefano Stabellini 	.probe = xencons_probe,
587cf8e019bSStefano Stabellini 	.remove = xencons_remove,
588cf8e019bSStefano Stabellini 	.resume = xencons_resume,
589cf8e019bSStefano Stabellini 	.otherend_changed = xencons_backend_changed,
59002391434SJuergen Gross 	.not_essential = true,
59195afae48SDavid Vrabel };
592cf8e019bSStefano Stabellini #endif /* CONFIG_HVC_XEN_FRONTEND */
593cf8e019bSStefano Stabellini 
xen_hvc_init(void)594cf8e019bSStefano Stabellini static int __init xen_hvc_init(void)
595cf8e019bSStefano Stabellini {
596cf8e019bSStefano Stabellini 	int r;
597cf8e019bSStefano Stabellini 	struct xencons_info *info;
598cf8e019bSStefano Stabellini 	const struct hv_ops *ops;
599cf8e019bSStefano Stabellini 
600cf8e019bSStefano Stabellini 	if (!xen_domain())
601cf8e019bSStefano Stabellini 		return -ENODEV;
602cf8e019bSStefano Stabellini 
603cf8e019bSStefano Stabellini 	if (xen_initial_domain()) {
604cf8e019bSStefano Stabellini 		ops = &dom0_hvc_ops;
605cf8e019bSStefano Stabellini 		r = xen_initial_domain_console_init();
606cf8e019bSStefano Stabellini 		if (r < 0)
607202f4d78SDavid Woodhouse 			goto register_fe;
608cf8e019bSStefano Stabellini 		info = vtermno_to_xencons(HVC_COOKIE);
609cf8e019bSStefano Stabellini 	} else {
610cf8e019bSStefano Stabellini 		ops = &domU_hvc_ops;
611cf8e019bSStefano Stabellini 		if (xen_hvm_domain())
612cf8e019bSStefano Stabellini 			r = xen_hvm_console_init();
613cf8e019bSStefano Stabellini 		else
614cf8e019bSStefano Stabellini 			r = xen_pv_console_init();
615cf8e019bSStefano Stabellini 		if (r < 0)
616202f4d78SDavid Woodhouse 			goto register_fe;
617cf8e019bSStefano Stabellini 
618cf8e019bSStefano Stabellini 		info = vtermno_to_xencons(HVC_COOKIE);
619fe415186SJuergen Gross 		info->irq = bind_evtchn_to_irq_lateeoi(info->evtchn);
620cf8e019bSStefano Stabellini 	}
621cf8e019bSStefano Stabellini 	if (info->irq < 0)
622cf8e019bSStefano Stabellini 		info->irq = 0; /* NO_IRQ */
623cf8e019bSStefano Stabellini 	else
624cf8e019bSStefano Stabellini 		irq_set_noprobe(info->irq);
625cf8e019bSStefano Stabellini 
626cf8e019bSStefano Stabellini 	info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256);
627cf8e019bSStefano Stabellini 	if (IS_ERR(info->hvc)) {
628c0dccad8SRoger Pau Monne 		unsigned long flags;
629c0dccad8SRoger Pau Monne 
630cf8e019bSStefano Stabellini 		r = PTR_ERR(info->hvc);
631c0dccad8SRoger Pau Monne 		spin_lock_irqsave(&xencons_lock, flags);
632cf8e019bSStefano Stabellini 		list_del(&info->list);
633c0dccad8SRoger Pau Monne 		spin_unlock_irqrestore(&xencons_lock, flags);
634cf8e019bSStefano Stabellini 		if (info->irq)
63511e5dac7SDavid Woodhouse 			evtchn_put(info->evtchn);
636cf8e019bSStefano Stabellini 		kfree(info);
637cf8e019bSStefano Stabellini 		return r;
638cf8e019bSStefano Stabellini 	}
639cf8e019bSStefano Stabellini 
640cf8e019bSStefano Stabellini 	r = 0;
641202f4d78SDavid Woodhouse  register_fe:
642cf8e019bSStefano Stabellini #ifdef CONFIG_HVC_XEN_FRONTEND
643cf8e019bSStefano Stabellini 	r = xenbus_register_frontend(&xencons_driver);
644cf8e019bSStefano Stabellini #endif
645cf8e019bSStefano Stabellini 	return r;
646cf8e019bSStefano Stabellini }
6474fedd0bfSPaul Gortmaker device_initcall(xen_hvc_init);
648728674a7SGreg Kroah-Hartman 
xen_cons_init(void)649728674a7SGreg Kroah-Hartman static int xen_cons_init(void)
650728674a7SGreg Kroah-Hartman {
651eb5ef071SStefano Stabellini 	const struct hv_ops *ops;
652728674a7SGreg Kroah-Hartman 
653eb5ef071SStefano Stabellini 	if (!xen_domain())
654728674a7SGreg Kroah-Hartman 		return 0;
655728674a7SGreg Kroah-Hartman 
656728674a7SGreg Kroah-Hartman 	if (xen_initial_domain())
657728674a7SGreg Kroah-Hartman 		ops = &dom0_hvc_ops;
658eb5ef071SStefano Stabellini 	else {
65902e19f9cSStefano Stabellini 		int r;
660728674a7SGreg Kroah-Hartman 		ops = &domU_hvc_ops;
661728674a7SGreg Kroah-Hartman 
66202e19f9cSStefano Stabellini 		if (xen_hvm_domain())
66302e19f9cSStefano Stabellini 			r = xen_hvm_console_init();
664eb5ef071SStefano Stabellini 		else
66502e19f9cSStefano Stabellini 			r = xen_pv_console_init();
66602e19f9cSStefano Stabellini 		if (r < 0)
66702e19f9cSStefano Stabellini 			return r;
668eb5ef071SStefano Stabellini 	}
669eb5ef071SStefano Stabellini 
670728674a7SGreg Kroah-Hartman 	hvc_instantiate(HVC_COOKIE, 0, ops);
671728674a7SGreg Kroah-Hartman 	return 0;
672728674a7SGreg Kroah-Hartman }
673728674a7SGreg Kroah-Hartman console_initcall(xen_cons_init);
674728674a7SGreg Kroah-Hartman 
675a4d7b75bSStefano Stabellini #ifdef CONFIG_X86
xen_hvm_early_write(uint32_t vtermno,const char * str,int len)676a4d7b75bSStefano Stabellini static void xen_hvm_early_write(uint32_t vtermno, const char *str, int len)
677a4d7b75bSStefano Stabellini {
678a4d7b75bSStefano Stabellini 	if (xen_cpuid_base())
679a4d7b75bSStefano Stabellini 		outsb(0xe9, str, len);
680a4d7b75bSStefano Stabellini }
681a4d7b75bSStefano Stabellini #else
xen_hvm_early_write(uint32_t vtermno,const char * str,int len)682a4d7b75bSStefano Stabellini static void xen_hvm_early_write(uint32_t vtermno, const char *str, int len) { }
683a4d7b75bSStefano Stabellini #endif
684a4d7b75bSStefano Stabellini 
685728674a7SGreg Kroah-Hartman #ifdef CONFIG_EARLY_PRINTK
xenboot_console_setup(struct console * console,char * string)6866f2fdb29SSergey Senozhatsky static int __init xenboot_console_setup(struct console *console, char *string)
6875de738b3SStefano Stabellini {
6885de738b3SStefano Stabellini 	static struct xencons_info xenboot;
6895de738b3SStefano Stabellini 
69042bc9716SJan Beulich 	if (xen_initial_domain() || !xen_pv_domain())
6915de738b3SStefano Stabellini 		return 0;
6925de738b3SStefano Stabellini 
6935de738b3SStefano Stabellini 	return xencons_info_pv_init(&xenboot, 0);
6945de738b3SStefano Stabellini }
6955de738b3SStefano Stabellini 
xenboot_write_console(struct console * console,const char * string,unsigned len)696728674a7SGreg Kroah-Hartman static void xenboot_write_console(struct console *console, const char *string,
697728674a7SGreg Kroah-Hartman 				  unsigned len)
698728674a7SGreg Kroah-Hartman {
699728674a7SGreg Kroah-Hartman 	unsigned int linelen, off = 0;
700728674a7SGreg Kroah-Hartman 	const char *pos;
701728674a7SGreg Kroah-Hartman 
702adf330a7SJan Beulich 	if (dom0_write_console(0, string, len) >= 0)
703adf330a7SJan Beulich 		return;
704adf330a7SJan Beulich 
705a4d7b75bSStefano Stabellini 	if (!xen_pv_domain()) {
706a4d7b75bSStefano Stabellini 		xen_hvm_early_write(0, string, len);
707eb5ef071SStefano Stabellini 		return;
708a4d7b75bSStefano Stabellini 	}
709eb5ef071SStefano Stabellini 
710adf330a7SJan Beulich 	if (domU_write_console(0, "(early) ", 8) < 0)
711728674a7SGreg Kroah-Hartman 		return;
712728674a7SGreg Kroah-Hartman 	while (off < len && NULL != (pos = strchr(string+off, '\n'))) {
713728674a7SGreg Kroah-Hartman 		linelen = pos-string+off;
714728674a7SGreg Kroah-Hartman 		if (off + linelen > len)
715728674a7SGreg Kroah-Hartman 			break;
716728674a7SGreg Kroah-Hartman 		domU_write_console(0, string+off, linelen);
717728674a7SGreg Kroah-Hartman 		domU_write_console(0, "\r\n", 2);
718728674a7SGreg Kroah-Hartman 		off += linelen + 1;
719728674a7SGreg Kroah-Hartman 	}
720728674a7SGreg Kroah-Hartman 	if (off < len)
721728674a7SGreg Kroah-Hartman 		domU_write_console(0, string+off, len-off);
722728674a7SGreg Kroah-Hartman }
723728674a7SGreg Kroah-Hartman 
724728674a7SGreg Kroah-Hartman struct console xenboot_console = {
725728674a7SGreg Kroah-Hartman 	.name		= "xenboot",
726728674a7SGreg Kroah-Hartman 	.write		= xenboot_write_console,
7276f2fdb29SSergey Senozhatsky 	.setup		= xenboot_console_setup,
728728674a7SGreg Kroah-Hartman 	.flags		= CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
729a9fbf4d5SDavid Vrabel 	.index		= -1,
730728674a7SGreg Kroah-Hartman };
731728674a7SGreg Kroah-Hartman #endif	/* CONFIG_EARLY_PRINTK */
732728674a7SGreg Kroah-Hartman 
xen_raw_console_write(const char * str)733728674a7SGreg Kroah-Hartman void xen_raw_console_write(const char *str)
734728674a7SGreg Kroah-Hartman {
73504b772d2SKonrad Rzeszutek Wilk 	ssize_t len = strlen(str);
73604b772d2SKonrad Rzeszutek Wilk 	int rc = 0;
73704b772d2SKonrad Rzeszutek Wilk 
73804b772d2SKonrad Rzeszutek Wilk 	if (xen_domain()) {
73904b772d2SKonrad Rzeszutek Wilk 		rc = dom0_write_console(0, str, len);
740a4d7b75bSStefano Stabellini 		if (rc != -ENOSYS || !xen_hvm_domain())
741a4d7b75bSStefano Stabellini 			return;
74204b772d2SKonrad Rzeszutek Wilk 	}
743a4d7b75bSStefano Stabellini 	xen_hvm_early_write(0, str, len);
744728674a7SGreg Kroah-Hartman }
745728674a7SGreg Kroah-Hartman 
xen_raw_printk(const char * fmt,...)746728674a7SGreg Kroah-Hartman void xen_raw_printk(const char *fmt, ...)
747728674a7SGreg Kroah-Hartman {
748728674a7SGreg Kroah-Hartman 	static char buf[512];
749728674a7SGreg Kroah-Hartman 	va_list ap;
750728674a7SGreg Kroah-Hartman 
751728674a7SGreg Kroah-Hartman 	va_start(ap, fmt);
752728674a7SGreg Kroah-Hartman 	vsnprintf(buf, sizeof(buf), fmt, ap);
753728674a7SGreg Kroah-Hartman 	va_end(ap);
754728674a7SGreg Kroah-Hartman 
755728674a7SGreg Kroah-Hartman 	xen_raw_console_write(buf);
756728674a7SGreg Kroah-Hartman }
75716e506efSStefano Stabellini 
xenboot_earlycon_write(struct console * console,const char * string,unsigned len)75816e506efSStefano Stabellini static void xenboot_earlycon_write(struct console *console,
75916e506efSStefano Stabellini 				  const char *string,
76016e506efSStefano Stabellini 				  unsigned len)
76116e506efSStefano Stabellini {
76216e506efSStefano Stabellini 	dom0_write_console(0, string, len);
76316e506efSStefano Stabellini }
76416e506efSStefano Stabellini 
xenboot_earlycon_setup(struct earlycon_device * device,const char * opt)76516e506efSStefano Stabellini static int __init xenboot_earlycon_setup(struct earlycon_device *device,
76616e506efSStefano Stabellini 					    const char *opt)
76716e506efSStefano Stabellini {
76816e506efSStefano Stabellini 	device->con->write = xenboot_earlycon_write;
76916e506efSStefano Stabellini 	return 0;
77016e506efSStefano Stabellini }
77116e506efSStefano Stabellini EARLYCON_DECLARE(xenboot, xenboot_earlycon_setup);
772