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