brintos

brintos / linux-shallow public Read only

0
0
Text · 17.5 KiB · 388a71a Raw
773 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * xen console driver interface to hvc_console.c4 *5 * (c) 2007 Gerd Hoffmann <kraxel@suse.de>6 */7 8#include <linux/console.h>9#include <linux/delay.h>10#include <linux/err.h>11#include <linux/irq.h>12#include <linux/init.h>13#include <linux/types.h>14#include <linux/list.h>15#include <linux/serial_core.h>16 17#include <asm/io.h>18#include <asm/xen/hypervisor.h>19 20#include <xen/xen.h>21#include <xen/interface/xen.h>22#include <xen/hvm.h>23#include <xen/grant_table.h>24#include <xen/page.h>25#include <xen/events.h>26#include <xen/interface/io/console.h>27#include <xen/interface/sched.h>28#include <xen/hvc-console.h>29#include <xen/xenbus.h>30 31#include "hvc_console.h"32 33#define HVC_COOKIE   0x58656e /* "Xen" in hex */34 35struct xencons_info {36	struct list_head list;37	struct xenbus_device *xbdev;38	struct xencons_interface *intf;39	unsigned int evtchn;40	XENCONS_RING_IDX out_cons;41	unsigned int out_cons_same;42	struct hvc_struct *hvc;43	int irq;44	int vtermno;45	grant_ref_t gntref;46	spinlock_t ring_lock;47};48 49static LIST_HEAD(xenconsoles);50static DEFINE_SPINLOCK(xencons_lock);51 52/* ------------------------------------------------------------------ */53 54static struct xencons_info *vtermno_to_xencons(int vtermno)55{56	struct xencons_info *entry, *ret = NULL;57	unsigned long flags;58 59	spin_lock_irqsave(&xencons_lock, flags);60	if (list_empty(&xenconsoles)) {61		spin_unlock_irqrestore(&xencons_lock, flags);62		return NULL;63	}64 65	list_for_each_entry(entry, &xenconsoles, list) {66		if (entry->vtermno == vtermno) {67			ret  = entry;68			break;69		}70	}71	spin_unlock_irqrestore(&xencons_lock, flags);72 73	return ret;74}75 76static inline int xenbus_devid_to_vtermno(int devid)77{78	return devid + HVC_COOKIE;79}80 81static inline void notify_daemon(struct xencons_info *cons)82{83	/* Use evtchn: this is called early, before irq is set up. */84	notify_remote_via_evtchn(cons->evtchn);85}86 87static ssize_t __write_console(struct xencons_info *xencons,88			       const u8 *data, size_t len)89{90	XENCONS_RING_IDX cons, prod;91	struct xencons_interface *intf = xencons->intf;92	unsigned long flags;93	size_t sent = 0;94 95	spin_lock_irqsave(&xencons->ring_lock, flags);96	cons = intf->out_cons;97	prod = intf->out_prod;98	mb();			/* update queue values before going on */99 100	if ((prod - cons) > sizeof(intf->out)) {101		spin_unlock_irqrestore(&xencons->ring_lock, flags);102		pr_err_once("xencons: Illegal ring page indices");103		return -EINVAL;104	}105 106	while ((sent < len) && ((prod - cons) < sizeof(intf->out)))107		intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];108 109	wmb();			/* write ring before updating pointer */110	intf->out_prod = prod;111	spin_unlock_irqrestore(&xencons->ring_lock, flags);112 113	if (sent)114		notify_daemon(xencons);115	return sent;116}117 118static ssize_t domU_write_console(uint32_t vtermno, const u8 *data, size_t len)119{120	struct xencons_info *cons = vtermno_to_xencons(vtermno);121	size_t ret = len;122 123	if (cons == NULL)124		return -EINVAL;125 126	/*127	 * Make sure the whole buffer is emitted, polling if128	 * necessary.  We don't ever want to rely on the hvc daemon129	 * because the most interesting console output is when the130	 * kernel is crippled.131	 */132	while (len) {133		ssize_t sent = __write_console(cons, data, len);134 135		if (sent < 0)136			return sent;137 138		data += sent;139		len -= sent;140 141		if (unlikely(len))142			HYPERVISOR_sched_op(SCHEDOP_yield, NULL);143	}144 145	return ret;146}147 148static ssize_t domU_read_console(uint32_t vtermno, u8 *buf, size_t len)149{150	struct xencons_interface *intf;151	XENCONS_RING_IDX cons, prod;152	struct xencons_info *xencons = vtermno_to_xencons(vtermno);153	unsigned int eoiflag = 0;154	unsigned long flags;155	size_t recv = 0;156 157	if (xencons == NULL)158		return -EINVAL;159	intf = xencons->intf;160 161	spin_lock_irqsave(&xencons->ring_lock, flags);162	cons = intf->in_cons;163	prod = intf->in_prod;164	mb();			/* get pointers before reading ring */165 166	if ((prod - cons) > sizeof(intf->in)) {167		spin_unlock_irqrestore(&xencons->ring_lock, flags);168		pr_err_once("xencons: Illegal ring page indices");169		return -EINVAL;170	}171 172	while (cons != prod && recv < len)173		buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];174 175	mb();			/* read ring before consuming */176	intf->in_cons = cons;177 178	/*179	 * When to mark interrupt having been spurious:180	 * - there was no new data to be read, and181	 * - the backend did not consume some output bytes, and182	 * - the previous round with no read data didn't see consumed bytes183	 *   (we might have a race with an interrupt being in flight while184	 *   updating xencons->out_cons, so account for that by allowing one185	 *   round without any visible reason)186	 */187	if (intf->out_cons != xencons->out_cons) {188		xencons->out_cons = intf->out_cons;189		xencons->out_cons_same = 0;190	}191	if (!recv && xencons->out_cons_same++ > 1) {192		eoiflag = XEN_EOI_FLAG_SPURIOUS;193	}194	spin_unlock_irqrestore(&xencons->ring_lock, flags);195 196	if (recv) {197		notify_daemon(xencons);198	}199 200	xen_irq_lateeoi(xencons->irq, eoiflag);201 202	return recv;203}204 205static const struct hv_ops domU_hvc_ops = {206	.get_chars = domU_read_console,207	.put_chars = domU_write_console,208	.notifier_add = notifier_add_irq,209	.notifier_del = notifier_del_irq,210	.notifier_hangup = notifier_hangup_irq,211};212 213static ssize_t dom0_read_console(uint32_t vtermno, u8 *buf, size_t len)214{215	return HYPERVISOR_console_io(CONSOLEIO_read, len, buf);216}217 218/*219 * Either for a dom0 to write to the system console, or a domU with a220 * debug version of Xen221 */222static ssize_t dom0_write_console(uint32_t vtermno, const u8 *str, size_t len)223{224	int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (u8 *)str);225	if (rc < 0)226		return rc;227 228	return len;229}230 231static const struct hv_ops dom0_hvc_ops = {232	.get_chars = dom0_read_console,233	.put_chars = dom0_write_console,234	.notifier_add = notifier_add_irq,235	.notifier_del = notifier_del_irq,236	.notifier_hangup = notifier_hangup_irq,237};238 239static int xen_hvm_console_init(void)240{241	int r;242	uint64_t v = 0;243	unsigned long gfn, flags;244	struct xencons_info *info;245 246	if (!xen_hvm_domain())247		return -ENODEV;248 249	info = vtermno_to_xencons(HVC_COOKIE);250	if (!info) {251		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);252		if (!info)253			return -ENOMEM;254		spin_lock_init(&info->ring_lock);255	} else if (info->intf != NULL) {256		/* already configured */257		return 0;258	}259	/*260	 * If the toolstack (or the hypervisor) hasn't set these values, the261	 * default value is 0. Even though gfn = 0 and evtchn = 0 are262	 * theoretically correct values, in practice they never are and they263	 * mean that a legacy toolstack hasn't initialized the pv console correctly.264	 */265	r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);266	if (r < 0 || v == 0)267		goto err;268	info->evtchn = v;269	v = 0;270	r = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v);271	if (r < 0 || v == 0)272		goto err;273	gfn = v;274	info->intf = memremap(gfn << XEN_PAGE_SHIFT, XEN_PAGE_SIZE, MEMREMAP_WB);275	if (info->intf == NULL)276		goto err;277	info->vtermno = HVC_COOKIE;278 279	spin_lock_irqsave(&xencons_lock, flags);280	list_add_tail(&info->list, &xenconsoles);281	spin_unlock_irqrestore(&xencons_lock, flags);282 283	return 0;284err:285	kfree(info);286	return -ENODEV;287}288 289static int xencons_info_pv_init(struct xencons_info *info, int vtermno)290{291	spin_lock_init(&info->ring_lock);292	info->evtchn = xen_start_info->console.domU.evtchn;293	/* GFN == MFN for PV guest */294	info->intf = gfn_to_virt(xen_start_info->console.domU.mfn);295	info->vtermno = vtermno;296 297	list_add_tail(&info->list, &xenconsoles);298 299	return 0;300}301 302static int xen_pv_console_init(void)303{304	struct xencons_info *info;305	unsigned long flags;306 307	if (!xen_pv_domain())308		return -ENODEV;309 310	if (!xen_start_info->console.domU.evtchn)311		return -ENODEV;312 313	info = vtermno_to_xencons(HVC_COOKIE);314	if (!info) {315		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);316		if (!info)317			return -ENOMEM;318	} else if (info->intf != NULL) {319		/* already configured */320		return 0;321	}322	spin_lock_irqsave(&xencons_lock, flags);323	xencons_info_pv_init(info, HVC_COOKIE);324	spin_unlock_irqrestore(&xencons_lock, flags);325 326	return 0;327}328 329static int xen_initial_domain_console_init(void)330{331	struct xencons_info *info;332	unsigned long flags;333 334	if (!xen_initial_domain())335		return -ENODEV;336 337	info = vtermno_to_xencons(HVC_COOKIE);338	if (!info) {339		info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);340		if (!info)341			return -ENOMEM;342		spin_lock_init(&info->ring_lock);343	}344 345	info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);346	info->vtermno = HVC_COOKIE;347 348	spin_lock_irqsave(&xencons_lock, flags);349	list_add_tail(&info->list, &xenconsoles);350	spin_unlock_irqrestore(&xencons_lock, flags);351 352	return 0;353}354 355static void xen_console_update_evtchn(struct xencons_info *info)356{357	if (xen_hvm_domain()) {358		uint64_t v = 0;359		int err;360 361		err = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);362		if (!err && v)363			info->evtchn = v;364	} else365		info->evtchn = xen_start_info->console.domU.evtchn;366}367 368void xen_console_resume(void)369{370	struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE);371	if (info != NULL && info->irq) {372		if (!xen_initial_domain())373			xen_console_update_evtchn(info);374		rebind_evtchn_irq(info->evtchn, info->irq);375	}376}377 378#ifdef CONFIG_HVC_XEN_FRONTEND379static void xencons_disconnect_backend(struct xencons_info *info)380{381	if (info->hvc != NULL)382		hvc_remove(info->hvc);383	info->hvc = NULL;384	if (info->irq > 0) {385		evtchn_put(info->evtchn);386		info->irq = 0;387		info->evtchn = 0;388	}389	/* evtchn_put() will also close it so this is only an error path */390	if (info->evtchn > 0)391		xenbus_free_evtchn(info->xbdev, info->evtchn);392	info->evtchn = 0;393	if (info->gntref > 0)394		gnttab_free_grant_references(info->gntref);395	info->gntref = 0;396}397 398static void xencons_free(struct xencons_info *info)399{400	free_page((unsigned long)info->intf);401	info->intf = NULL;402	info->vtermno = 0;403	kfree(info);404}405 406static int xen_console_remove(struct xencons_info *info)407{408	unsigned long flags;409 410	xencons_disconnect_backend(info);411	spin_lock_irqsave(&xencons_lock, flags);412	list_del(&info->list);413	spin_unlock_irqrestore(&xencons_lock, flags);414	if (info->xbdev != NULL)415		xencons_free(info);416	else {417		if (xen_hvm_domain())418			iounmap(info->intf);419		kfree(info);420	}421	return 0;422}423 424static void xencons_remove(struct xenbus_device *dev)425{426	xen_console_remove(dev_get_drvdata(&dev->dev));427}428 429static int xencons_connect_backend(struct xenbus_device *dev,430				  struct xencons_info *info)431{432	int ret, evtchn, devid, ref, irq;433	struct xenbus_transaction xbt;434	grant_ref_t gref_head;435 436	ret = xenbus_alloc_evtchn(dev, &evtchn);437	if (ret)438		return ret;439	info->evtchn = evtchn;440	irq = bind_evtchn_to_irq_lateeoi(evtchn);441	if (irq < 0)442		return irq;443	info->irq = irq;444	devid = dev->nodename[strlen(dev->nodename) - 1] - '0';445	info->hvc = hvc_alloc(xenbus_devid_to_vtermno(devid),446			irq, &domU_hvc_ops, 256);447	if (IS_ERR(info->hvc))448		return PTR_ERR(info->hvc);449	ret = gnttab_alloc_grant_references(1, &gref_head);450	if (ret < 0)451		return ret;452	info->gntref = gref_head;453	ref = gnttab_claim_grant_reference(&gref_head);454	if (ref < 0)455		return ref;456	gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id,457					virt_to_gfn(info->intf), 0);458 459 again:460	ret = xenbus_transaction_start(&xbt);461	if (ret) {462		xenbus_dev_fatal(dev, ret, "starting transaction");463		return ret;464	}465	ret = xenbus_printf(xbt, dev->nodename, "ring-ref", "%d", ref);466	if (ret)467		goto error_xenbus;468	ret = xenbus_printf(xbt, dev->nodename, "port", "%u",469			    evtchn);470	if (ret)471		goto error_xenbus;472	ret = xenbus_transaction_end(xbt, 0);473	if (ret) {474		if (ret == -EAGAIN)475			goto again;476		xenbus_dev_fatal(dev, ret, "completing transaction");477		return ret;478	}479 480	xenbus_switch_state(dev, XenbusStateInitialised);481	return 0;482 483 error_xenbus:484	xenbus_transaction_end(xbt, 1);485	xenbus_dev_fatal(dev, ret, "writing xenstore");486	return ret;487}488 489static int xencons_probe(struct xenbus_device *dev,490				  const struct xenbus_device_id *id)491{492	int ret, devid;493	struct xencons_info *info;494	unsigned long flags;495 496	devid = dev->nodename[strlen(dev->nodename) - 1] - '0';497	if (devid == 0)498		return -ENODEV;499 500	info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL);501	if (!info)502		return -ENOMEM;503	spin_lock_init(&info->ring_lock);504	dev_set_drvdata(&dev->dev, info);505	info->xbdev = dev;506	info->vtermno = xenbus_devid_to_vtermno(devid);507	info->intf = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);508	if (!info->intf)509		goto error_nomem;510 511	ret = xencons_connect_backend(dev, info);512	if (ret < 0)513		goto error;514	spin_lock_irqsave(&xencons_lock, flags);515	list_add_tail(&info->list, &xenconsoles);516	spin_unlock_irqrestore(&xencons_lock, flags);517 518	return 0;519 520 error_nomem:521	ret = -ENOMEM;522	xenbus_dev_fatal(dev, ret, "allocating device memory");523 error:524	xencons_disconnect_backend(info);525	xencons_free(info);526	return ret;527}528 529static int xencons_resume(struct xenbus_device *dev)530{531	struct xencons_info *info = dev_get_drvdata(&dev->dev);532 533	xencons_disconnect_backend(info);534	memset(info->intf, 0, XEN_PAGE_SIZE);535	return xencons_connect_backend(dev, info);536}537 538static void xencons_backend_changed(struct xenbus_device *dev,539				   enum xenbus_state backend_state)540{541	switch (backend_state) {542	case XenbusStateReconfiguring:543	case XenbusStateReconfigured:544	case XenbusStateInitialising:545	case XenbusStateInitialised:546	case XenbusStateUnknown:547		break;548 549	case XenbusStateInitWait:550		break;551 552	case XenbusStateConnected:553		xenbus_switch_state(dev, XenbusStateConnected);554		break;555 556	case XenbusStateClosed:557		if (dev->state == XenbusStateClosed)558			break;559		fallthrough;	/* Missed the backend's CLOSING state */560	case XenbusStateClosing: {561		struct xencons_info *info = dev_get_drvdata(&dev->dev);562 563		/*564		 * Don't tear down the evtchn and grant ref before the other565		 * end has disconnected, but do stop userspace from trying566		 * to use the device before we allow the backend to close.567		 */568		if (info->hvc) {569			hvc_remove(info->hvc);570			info->hvc = NULL;571		}572 573		xenbus_frontend_closed(dev);574		break;575	}576	}577}578 579static const struct xenbus_device_id xencons_ids[] = {580	{ "console" },581	{ "" }582};583 584static struct xenbus_driver xencons_driver = {585	.name = "xenconsole",586	.ids = xencons_ids,587	.probe = xencons_probe,588	.remove = xencons_remove,589	.resume = xencons_resume,590	.otherend_changed = xencons_backend_changed,591	.not_essential = true,592};593#endif /* CONFIG_HVC_XEN_FRONTEND */594 595static int __init xen_hvc_init(void)596{597	int r;598	struct xencons_info *info;599	const struct hv_ops *ops;600 601	if (!xen_domain())602		return -ENODEV;603 604	if (xen_initial_domain()) {605		ops = &dom0_hvc_ops;606		r = xen_initial_domain_console_init();607		if (r < 0)608			goto register_fe;609		info = vtermno_to_xencons(HVC_COOKIE);610	} else {611		ops = &domU_hvc_ops;612		if (xen_hvm_domain())613			r = xen_hvm_console_init();614		else615			r = xen_pv_console_init();616		if (r < 0)617			goto register_fe;618 619		info = vtermno_to_xencons(HVC_COOKIE);620		info->irq = bind_evtchn_to_irq_lateeoi(info->evtchn);621	}622	if (info->irq < 0)623		info->irq = 0; /* NO_IRQ */624	else625		irq_set_noprobe(info->irq);626 627	info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256);628	if (IS_ERR(info->hvc)) {629		unsigned long flags;630 631		r = PTR_ERR(info->hvc);632		spin_lock_irqsave(&xencons_lock, flags);633		list_del(&info->list);634		spin_unlock_irqrestore(&xencons_lock, flags);635		if (info->irq)636			evtchn_put(info->evtchn);637		kfree(info);638		return r;639	}640 641	r = 0;642 register_fe:643#ifdef CONFIG_HVC_XEN_FRONTEND644	r = xenbus_register_frontend(&xencons_driver);645#endif646	return r;647}648device_initcall(xen_hvc_init);649 650static int xen_cons_init(void)651{652	const struct hv_ops *ops;653 654	if (!xen_domain())655		return 0;656 657	if (xen_initial_domain())658		ops = &dom0_hvc_ops;659	else {660		int r;661		ops = &domU_hvc_ops;662 663		if (xen_hvm_domain())664			r = xen_hvm_console_init();665		else666			r = xen_pv_console_init();667		if (r < 0)668			return r;669	}670 671	hvc_instantiate(HVC_COOKIE, 0, ops);672	return 0;673}674console_initcall(xen_cons_init);675 676#ifdef CONFIG_X86677static void xen_hvm_early_write(uint32_t vtermno, const char *str, int len)678{679	if (xen_cpuid_base())680		outsb(0xe9, str, len);681}682#else683static void xen_hvm_early_write(uint32_t vtermno, const char *str, int len) { }684#endif685 686#ifdef CONFIG_EARLY_PRINTK687static int __init xenboot_console_setup(struct console *console, char *string)688{689	static struct xencons_info xenboot;690 691	if (xen_initial_domain() || !xen_pv_domain())692		return 0;693 694	return xencons_info_pv_init(&xenboot, 0);695}696 697static void xenboot_write_console(struct console *console, const char *string,698				  unsigned len)699{700	unsigned int linelen, off = 0;701	const char *pos;702 703	if (dom0_write_console(0, string, len) >= 0)704		return;705 706	if (!xen_pv_domain()) {707		xen_hvm_early_write(0, string, len);708		return;709	}710 711	if (domU_write_console(0, "(early) ", 8) < 0)712		return;713	while (off < len && NULL != (pos = strchr(string+off, '\n'))) {714		linelen = pos-string+off;715		if (off + linelen > len)716			break;717		domU_write_console(0, string+off, linelen);718		domU_write_console(0, "\r\n", 2);719		off += linelen + 1;720	}721	if (off < len)722		domU_write_console(0, string+off, len-off);723}724 725struct console xenboot_console = {726	.name		= "xenboot",727	.write		= xenboot_write_console,728	.setup		= xenboot_console_setup,729	.flags		= CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,730	.index		= -1,731};732#endif	/* CONFIG_EARLY_PRINTK */733 734void xen_raw_console_write(const char *str)735{736	ssize_t len = strlen(str);737	int rc = 0;738 739	if (xen_domain()) {740		rc = dom0_write_console(0, str, len);741		if (rc != -ENOSYS || !xen_hvm_domain())742			return;743	}744	xen_hvm_early_write(0, str, len);745}746 747void xen_raw_printk(const char *fmt, ...)748{749	static char buf[512];750	va_list ap;751 752	va_start(ap, fmt);753	vsnprintf(buf, sizeof(buf), fmt, ap);754	va_end(ap);755 756	xen_raw_console_write(buf);757}758 759static void xenboot_earlycon_write(struct console *console,760				  const char *string,761				  unsigned len)762{763	dom0_write_console(0, string, len);764}765 766static int __init xenboot_earlycon_setup(struct earlycon_device *device,767					    const char *opt)768{769	device->con->write = xenboot_earlycon_write;770	return 0;771}772EARLYCON_DECLARE(xenboot, xenboot_earlycon_setup);773