Home | History | Annotate | Line # | Download | only in xen
      1 /*	$NetBSD: xen_machdep.c,v 1.30 2026/10/11 10:53:06 mlelstv Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2006 Manuel Bouyer.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  *
     26  */
     27 
     28 /*
     29  *
     30  * Copyright (c) 2004 Christian Limpach.
     31  * All rights reserved.
     32  *
     33  * Redistribution and use in source and binary forms, with or without
     34  * modification, are permitted provided that the following conditions
     35  * are met:
     36  * 1. Redistributions of source code must retain the above copyright
     37  *    notice, this list of conditions and the following disclaimer.
     38  * 2. Redistributions in binary form must reproduce the above copyright
     39  *    notice, this list of conditions and the following disclaimer in the
     40  *    documentation and/or other materials provided with the distribution.
     41  *
     42  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     43  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     44  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     45  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     46  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     47  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     48  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     49  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     50  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     51  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     52  */
     53 
     54 
     55 #include <sys/cdefs.h>
     56 __KERNEL_RCSID(0, "$NetBSD: xen_machdep.c,v 1.30 2026/10/11 10:53:06 mlelstv Exp $");
     57 
     58 #include "opt_xen.h"
     59 
     60 #include <sys/param.h>
     61 #include <sys/systm.h>
     62 #include <sys/boot_flag.h>
     63 #include <sys/conf.h>
     64 #include <sys/disk.h>
     65 #include <sys/disklabel.h>
     66 #include <sys/device.h>
     67 #include <sys/mount.h>
     68 #include <sys/reboot.h>
     69 #include <sys/timetc.h>
     70 #include <sys/sysctl.h>
     71 #include <sys/pmf.h>
     72 #include <sys/xcall.h>
     73 
     74 #include <dev/cons.h>
     75 
     76 #include <xen/intr.h>
     77 #include <xen/hypervisor.h>
     78 #include <xen/shutdown_xenbus.h>
     79 #include <xen/include/public/version.h>
     80 
     81 #include <machine/pmap_private.h>
     82 
     83 #define DPRINTK(x) printk x
     84 #if 0
     85 #define DPRINTK(x)
     86 #endif
     87 
     88 #ifdef DEBUG_GEOM
     89 #define DPRINTF(a) printf a
     90 #else
     91 #define DPRINTF(a)
     92 #endif
     93 
     94 
     95 bool xen_suspend_allow;
     96 
     97 void
     98 xen_parse_cmdline(int what, union xen_cmdline_parseinfo *xcp)
     99 {
    100 	char _cmd_line[256], *cmd_line, *opt, *s;
    101 	int b, i, ipidx = 0;
    102 	uint32_t xi_ip[5];
    103 	size_t len;
    104 
    105 	len = strlcpy(_cmd_line, xen_start_info.cmd_line, sizeof(_cmd_line));
    106 	if (len > sizeof(_cmd_line)) {
    107 		printf("command line exceeded limit of 255 chars. Truncated.\n");
    108 	}
    109 	cmd_line = _cmd_line;
    110 
    111 	switch (what) {
    112 	case XEN_PARSE_BOOTDEV:
    113 		xcp->xcp_bootdev[0] = 0;
    114 		break;
    115 	case XEN_PARSE_CONSOLE:
    116 		xcp->xcp_console[0] = 0;
    117 		break;
    118 	}
    119 
    120 	while (cmd_line && *cmd_line) {
    121 		opt = cmd_line;
    122 		cmd_line = strchr(opt, ' ');
    123 		if (cmd_line)
    124 			*cmd_line = 0;
    125 
    126 		switch (what) {
    127 		case XEN_PARSE_BOOTDEV:
    128 			if (strncasecmp(opt, "bootdev=", 8) == 0) {
    129 				strncpy(xcp->xcp_bootdev, opt + 8,
    130 				    sizeof(xcp->xcp_bootdev));
    131 				break;
    132 			}
    133 			if (strncasecmp(opt, "root=", 5) == 0) {
    134 				strncpy(xcp->xcp_bootdev, opt + 5,
    135 				    sizeof(xcp->xcp_bootdev));
    136 				break;
    137 			}
    138 			break;
    139 
    140 		case XEN_PARSE_NETINFO:
    141 			if (xcp->xcp_netinfo.xi_root &&
    142 			    strncasecmp(opt, "nfsroot=", 8) == 0)
    143 				strncpy(xcp->xcp_netinfo.xi_root, opt + 8,
    144 				    MNAMELEN);
    145 
    146 			if (strncasecmp(opt, "ip=", 3) == 0) {
    147 				memset(xi_ip, 0, sizeof(xi_ip));
    148 				opt += 3;
    149 				ipidx = 0;
    150 				while (opt && *opt) {
    151 					s = opt;
    152 					opt = strchr(opt, ':');
    153 					if (opt)
    154 						*opt = 0;
    155 
    156 					switch (ipidx) {
    157 					case 0:	/* ip */
    158 					case 1:	/* nfs server */
    159 					case 2:	/* gw */
    160 					case 3:	/* mask */
    161 					case 4:	/* host */
    162 						if (*s == 0)
    163 							break;
    164 						for (i = 0; i < 4; i++) {
    165 							b = strtoul(s, &s, 10);
    166 							xi_ip[ipidx] = b + 256
    167 								* xi_ip[ipidx];
    168 							if (*s != '.')
    169 								break;
    170 							s++;
    171 						}
    172 						if (i < 3)
    173 							xi_ip[ipidx] = 0;
    174 						break;
    175 					case 5:	/* interface */
    176 						if (!strncmp(s, "xennet", 6))
    177 							s += 6;
    178 						else if (!strncmp(s, "eth", 3))
    179 							s += 3;
    180 						else
    181 							break;
    182 						if (xcp->xcp_netinfo.xi_ifno
    183 						    == strtoul(s, NULL, 10))
    184 							memcpy(xcp->
    185 							    xcp_netinfo.xi_ip,
    186 							    xi_ip,
    187 							    sizeof(xi_ip));
    188 						break;
    189 					}
    190 					ipidx++;
    191 
    192 					if (opt)
    193 						*opt++ = ':';
    194 				}
    195 			}
    196 			break;
    197 
    198 		case XEN_PARSE_CONSOLE:
    199 			if (strncasecmp(opt, "console=", 8) == 0)
    200 				strncpy(xcp->xcp_console, opt + 8,
    201 				    sizeof(xcp->xcp_console));
    202 			break;
    203 
    204 		case XEN_PARSE_BOOTFLAGS:
    205 			if (*opt == '-') {
    206 				opt++;
    207 				while(*opt != '\0') {
    208 					BOOT_FLAG(*opt, boothowto);
    209 					opt++;
    210 				}
    211 			}
    212 			break;
    213 		case XEN_PARSE_PCIBACK:
    214 			if (strncasecmp(opt, "pciback.hide=", 13) == 0)
    215 				strncpy(xcp->xcp_pcidevs, opt + 13,
    216 				    sizeof(xcp->xcp_pcidevs));
    217 			break;
    218 		}
    219 
    220 		if (cmd_line)
    221 			*cmd_line++ = ' ';
    222 	}
    223 }
    224 
    225 #ifdef XENPV
    226 
    227 static int sysctl_xen_suspend(SYSCTLFN_ARGS);
    228 static void xen_suspend_domain(void);
    229 static void xen_prepare_suspend(void);
    230 static void xen_prepare_resume(void);
    231 
    232 /*
    233  * this function sets up the machdep.xen.suspend sysctl(7) that
    234  * controls domain suspend/save.
    235  */
    236 void
    237 sysctl_xen_suspend_setup(void)
    238 {
    239 	const struct sysctlnode *node = NULL;
    240 
    241 	/*
    242 	 * dom0 implements sleep support through ACPI. It should not call
    243 	 * this function to register a suspend interface.
    244 	 */
    245 	KASSERT(!(xendomain_is_dom0()));
    246 
    247 	sysctl_createv(NULL, 0, NULL, &node,
    248 	    CTLFLAG_PERMANENT,
    249 	    CTLTYPE_NODE, "machdep", NULL,
    250 	    NULL, 0, NULL, 0,
    251 	    CTL_MACHDEP, CTL_EOL);
    252 
    253 	sysctl_createv(NULL, 0, &node, &node,
    254 	    CTLFLAG_PERMANENT,
    255 	    CTLTYPE_NODE, "xen",
    256 	    SYSCTL_DESCR("Xen top level node"),
    257 	    NULL, 0, NULL, 0,
    258 	    CTL_CREATE, CTL_EOL);
    259 
    260 	sysctl_createv(NULL, 0, &node, &node,
    261 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE | CTLFLAG_IMMEDIATE,
    262 	    CTLTYPE_INT, "suspend",
    263 	    SYSCTL_DESCR("Suspend/save current Xen domain"),
    264 	    sysctl_xen_suspend, 0, NULL, 0,
    265 	    CTL_CREATE, CTL_EOL);
    266 }
    267 
    268 static int
    269 sysctl_xen_suspend(SYSCTLFN_ARGS)
    270 {
    271 	int error;
    272 	struct sysctlnode node;
    273 
    274 	node = *rnode;
    275 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    276 
    277 	if (error || newp == NULL)
    278 		return error;
    279 
    280 	/* only allow domain to suspend when dom0 instructed to do so */
    281 	if (xen_suspend_allow == false)
    282 		return EAGAIN;
    283 
    284 	xen_suspend_domain();
    285 
    286 	return 0;
    287 
    288 }
    289 
    290 static void xen_suspendclocks_xc(void *, void*);
    291 static void xen_resumeclocks_xc(void *, void*);
    292 
    293 /*
    294  * Last operations before suspending domain
    295  */
    296 static void
    297 xen_prepare_suspend(void)
    298 {
    299 
    300 	kpreempt_disable();
    301 
    302 	pmap_xen_suspend();
    303 	xc_wait(xc_broadcast(0, &xen_suspendclocks_xc, NULL, NULL));
    304 
    305 	/*
    306 	 * save/restore code does not translate these MFNs to their
    307 	 * associated PFNs, so we must do it
    308 	 */
    309 	xen_start_info.store_mfn =
    310 	    atop(xpmap_mtop(ptoa(xen_start_info.store_mfn)));
    311 	xen_start_info.console_mfn =
    312 	    atop(xpmap_mtop(ptoa(xen_start_info.console_mfn)));
    313 
    314 	DPRINTK(("suspending domain\n"));
    315 	aprint_verbose("suspending domain\n");
    316 
    317 	/* invalidate the shared_info page */
    318 	if (HYPERVISOR_update_va_mapping((vaddr_t)HYPERVISOR_shared_info,
    319 	    0, UVMF_INVLPG)) {
    320 		DPRINTK(("HYPERVISOR_shared_info page invalidation failed"));
    321 		HYPERVISOR_crash();
    322 	}
    323 
    324 }
    325 
    326 static void
    327 xen_suspendclocks_xc(void *a, void *b)
    328 {
    329 
    330 	kpreempt_disable();
    331 	xen_suspendclocks(curcpu());
    332 	kpreempt_enable();
    333 }
    334 
    335 /*
    336  * First operations before restoring domain context
    337  */
    338 static void
    339 xen_prepare_resume(void)
    340 {
    341 	/* map the new shared_info page */
    342 	if (HYPERVISOR_update_va_mapping((vaddr_t)HYPERVISOR_shared_info,
    343 	    xen_start_info.shared_info | PTE_W | PTE_P,
    344 	    UVMF_INVLPG)) {
    345 		DPRINTK(("could not map new shared info page"));
    346 		HYPERVISOR_crash();
    347 	}
    348 
    349 	pmap_xen_resume();
    350 
    351 	if (xen_start_info.nr_pages != physmem) {
    352 		/*
    353 		 * XXX JYM for now, we crash - fix it with memory
    354 		 * hotplug when supported
    355 		 */
    356 		DPRINTK(("xen_start_info.nr_pages != physmem"));
    357 		HYPERVISOR_crash();
    358 	}
    359 
    360 	DPRINTK(("preparing domain resume\n"));
    361 	aprint_verbose("preparing domain resume\n");
    362 
    363 	xen_suspend_allow = false;
    364 
    365 	xc_wait(xc_broadcast(0, xen_resumeclocks_xc, NULL, NULL));
    366 
    367 	kpreempt_enable();
    368 
    369 }
    370 
    371 static void
    372 xen_resumeclocks_xc(void *a, void *b)
    373 {
    374 
    375 	kpreempt_disable();
    376 	xen_resumeclocks(curcpu());
    377 	kpreempt_enable();
    378 }
    379 
    380 static void
    381 xen_suspend_domain(void)
    382 {
    383 	paddr_t mfn;
    384 	int s = splvm(); /* XXXSMP */
    385 
    386 	/*
    387 	 * console becomes unavailable when suspended, so
    388 	 * direct communications to domain are hampered from there on.
    389 	 * We can only rely on low level primitives like printk(), until
    390 	 * console is fully restored
    391 	 */
    392 	if (!pmf_system_suspend(PMF_Q_NONE)) {
    393 		DPRINTK(("devices suspend failed"));
    394 		HYPERVISOR_crash();
    395 	}
    396 
    397 	/*
    398 	 * obtain the MFN of the start_info page now, as we will not be
    399 	 * able to do it once pmap is locked
    400 	 */
    401 	pmap_extract_ma(pmap_kernel(), (vaddr_t)&xen_start_info, &mfn);
    402 	mfn >>= PAGE_SHIFT;
    403 
    404 	xen_prepare_suspend();
    405 
    406 	DPRINTK(("calling HYPERVISOR_suspend()\n"));
    407 	if (HYPERVISOR_suspend(mfn) != 0) {
    408 	/* XXX JYM: implement checkpoint/snapshot (ret == 1) */
    409 		DPRINTK(("HYPERVISOR_suspend() failed"));
    410 		HYPERVISOR_crash();
    411 	}
    412 
    413 	DPRINTK(("left HYPERVISOR_suspend()\n"));
    414 
    415 	xen_prepare_resume();
    416 
    417 	DPRINTK(("resuming devices\n"));
    418 	if (!pmf_system_resume(PMF_Q_NONE)) {
    419 		DPRINTK(("devices resume failed\n"));
    420 		HYPERVISOR_crash();
    421 	}
    422 
    423 	splx(s);
    424 
    425 	/* xencons is back online, we can print to console */
    426 	aprint_verbose("domain resumed\n");
    427 
    428 }
    429 #endif /* XENPV */
    430 
    431 #define PRINTK_BUFSIZE 1024
    432 void
    433 printk(const char *fmt, ...)
    434 {
    435 	va_list ap;
    436 	int ret;
    437 	static char buf[PRINTK_BUFSIZE];
    438 
    439 	va_start(ap, fmt);
    440 	ret = vsnprintf(buf, PRINTK_BUFSIZE - 1, fmt, ap);
    441 	va_end(ap);
    442 	buf[ret] = 0;
    443 	(void)HYPERVISOR_console_io(CONSOLEIO_write, ret, buf);
    444 }
    445 
    446 static int early_xenconscn_getc(dev_t);
    447 static void early_xenconscn_putc(dev_t, int);
    448 static void early_xenconscn_pollc(dev_t, int);
    449 
    450 static struct consdev early_xencons = {
    451 	NULL, NULL,
    452 	early_xenconscn_getc, early_xenconscn_putc, early_xenconscn_pollc,
    453 	NULL, NULL, NULL, NODEV, CN_NORMAL
    454 };
    455 
    456 void
    457 xen_early_console(void)
    458 {
    459 	cn_tab = &early_xencons; /* fallback console */
    460 }
    461 
    462 static int
    463 early_xenconscn_getc(dev_t dev)
    464 {
    465 	while(1)
    466 		;
    467 	return -1;
    468 }
    469 
    470 static void
    471 early_xenconscn_putc(dev_t dev, int c)
    472 {
    473 	printk("%c", c);
    474 }
    475 
    476 static void
    477 early_xenconscn_pollc(dev_t dev, int on)
    478 {
    479 	return;
    480 }
    481 bool xen_feature_tables[XENFEAT_NR_SUBMAPS * 32];
    482 
    483 void
    484 xen_init_features(void)
    485 {
    486 	xen_feature_info_t features;
    487 
    488 	for (int sm = 0; sm < XENFEAT_NR_SUBMAPS; sm++) {
    489 		features.submap_idx = sm;
    490 		if (HYPERVISOR_xen_version(XENVER_get_features, &features) < 0)
    491 			break;
    492 		for (int f = 0; f < 32; f++) {
    493 			xen_feature_tables[sm * 32 + f] =
    494 			    (features.submap & (1 << f)) ? 1 : 0;
    495 		}
    496 	}
    497 }
    498 
    499 /*
    500  * Attempt to find the device from which we were booted.
    501  */
    502 
    503 static int
    504 is_valid_disk(device_t dv)
    505 {
    506 	if (device_class(dv) != DV_DISK)
    507 		return (0);
    508 
    509 	return (device_is_a(dv, "dk") ||
    510 		device_is_a(dv, "sd") ||
    511 		device_is_a(dv, "wd") ||
    512 		device_is_a(dv, "ld") ||
    513 		device_is_a(dv, "ed") ||
    514 		device_is_a(dv, "xbd"));
    515 }
    516 
    517 void
    518 xen_bootconf(void)
    519 {
    520 	device_t dv;
    521 	deviter_t di;
    522 	union xen_cmdline_parseinfo xcp;
    523 	static char bootspecbuf[sizeof(xcp.xcp_bootdev)];
    524 
    525 	if (booted_device) {
    526 		DPRINTF(("%s: preset booted_device: %s\n", __func__, device_xname(booted_device)));
    527 		return;
    528 	}
    529 
    530 	xen_parse_cmdline(XEN_PARSE_BOOTDEV, &xcp);
    531 
    532 	for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
    533 	     dv != NULL;
    534 	     dv = deviter_next(&di)) {
    535 		bool is_ifnet, is_disk;
    536 		const char *devname;
    537 
    538 		is_ifnet = (device_class(dv) == DV_IFNET);
    539 		is_disk = is_valid_disk(dv);
    540 		devname = device_xname(dv);
    541 
    542 		if (!is_ifnet && !is_disk)
    543 			continue;
    544 
    545 		if (is_disk && xcp.xcp_bootdev[0] == 0) {
    546 			booted_device = dv;
    547 			break;
    548 		}
    549 
    550 		if (is_disk) {
    551 			size_t len;
    552 			int part;
    553 
    554 			len = strlen(devname);
    555 			if  (strncmp(xcp.xcp_bootdev, devname, len) != 0)
    556 				continue;
    557 
    558 			if (xcp.xcp_bootdev[len] != '\0') {
    559 				part = xcp.xcp_bootdev[len] - 'a';
    560 				if (part < 0 || part >= MAXPARTITIONS)
    561 					continue;
    562 
    563 				if (xcp.xcp_bootdev[len+1] != '\0')
    564 					continue;
    565 
    566 				booted_partition = part;
    567 				DPRINTF(("%s: booted_partition: %d\n", __func__, booted_partition));
    568 			}
    569 		} else {
    570 			if  (strcmp(xcp.xcp_bootdev, devname) != 0)
    571 				continue;
    572 		}
    573 
    574 		booted_device = dv;
    575 		booted_method = "bootinfo/bootdev";
    576 		break;
    577 	}
    578 	deviter_release(&di);
    579 
    580 	if (booted_device) {
    581 		DPRINTF(("%s: booted_device: %s\n", __func__, device_xname(booted_device)));
    582 		return;
    583 	}
    584 
    585 	/*
    586 	 * not a boot device name, pass through to MI code
    587 	 */
    588 	if (xcp.xcp_bootdev[0] != '\0') {
    589 		strlcpy(bootspecbuf, xcp.xcp_bootdev, sizeof(bootspecbuf));
    590 		bootspec = bootspecbuf;
    591 		booted_method = "bootinfo/bootspec";
    592 		DPRINTF(("%s: bootspec: %s\n", __func__, bootspec));
    593 		return;
    594 	}
    595 }
    596