Home | History | Annotate | Line # | Download | only in xen
      1 /*	$NetBSD: xen_machdep.c,v 1.12 2012/07/22 19:30:19 jym 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.12 2012/07/22 19:30:19 jym 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/mount.h>
     64 #include <sys/reboot.h>
     65 #include <sys/timetc.h>
     66 #include <sys/sysctl.h>
     67 #include <sys/pmf.h>
     68 
     69 #include <xen/hypervisor.h>
     70 #include <xen/shutdown_xenbus.h>
     71 
     72 #define DPRINTK(x) printk x
     73 #if 0
     74 #define DPRINTK(x)
     75 #endif
     76 
     77 u_int	tsc_get_timecount(struct timecounter *);
     78 
     79 bool xen_suspend_allow;
     80 
     81 extern uint64_t tsc_freq;	/* XXX */
     82 
     83 static int sysctl_xen_suspend(SYSCTLFN_ARGS);
     84 static void xen_suspend_domain(void);
     85 static void xen_prepare_suspend(void);
     86 static void xen_prepare_resume(void);
     87 
     88 void
     89 xen_parse_cmdline(int what, union xen_cmdline_parseinfo *xcp)
     90 {
     91 	char _cmd_line[256], *cmd_line, *opt, *s;
     92 	int b, i, ipidx = 0;
     93 	uint32_t xi_ip[5];
     94 	size_t len;
     95 
     96 	len = strlcpy(_cmd_line, xen_start_info.cmd_line, sizeof(_cmd_line));
     97 	if (len > sizeof(_cmd_line)) {
     98 		printf("command line exceeded limit of 255 chars. Truncated.\n");
     99 	}
    100 	cmd_line = _cmd_line;
    101 
    102 	switch (what) {
    103 	case XEN_PARSE_BOOTDEV:
    104 		xcp->xcp_bootdev[0] = 0;
    105 		break;
    106 	case XEN_PARSE_CONSOLE:
    107 		xcp->xcp_console[0] = 0;
    108 		break;
    109 	}
    110 
    111 	while (cmd_line && *cmd_line) {
    112 		opt = cmd_line;
    113 		cmd_line = strchr(opt, ' ');
    114 		if (cmd_line)
    115 			*cmd_line = 0;
    116 
    117 		switch (what) {
    118 		case XEN_PARSE_BOOTDEV:
    119 			if (strncasecmp(opt, "bootdev=", 8) == 0) {
    120 				strncpy(xcp->xcp_bootdev, opt + 8,
    121 				    sizeof(xcp->xcp_bootdev));
    122 				break;
    123 			}
    124 			if (strncasecmp(opt, "root=", 5) == 0) {
    125 				strncpy(xcp->xcp_bootdev, opt + 5,
    126 				    sizeof(xcp->xcp_bootdev));
    127 				break;
    128 			}
    129 			break;
    130 
    131 		case XEN_PARSE_NETINFO:
    132 			if (xcp->xcp_netinfo.xi_root &&
    133 			    strncasecmp(opt, "nfsroot=", 8) == 0)
    134 				strncpy(xcp->xcp_netinfo.xi_root, opt + 8,
    135 				    MNAMELEN);
    136 
    137 			if (strncasecmp(opt, "ip=", 3) == 0) {
    138 				memset(xi_ip, 0, sizeof(xi_ip));
    139 				opt += 3;
    140 				ipidx = 0;
    141 				while (opt && *opt) {
    142 					s = opt;
    143 					opt = strchr(opt, ':');
    144 					if (opt)
    145 						*opt = 0;
    146 
    147 					switch (ipidx) {
    148 					case 0:	/* ip */
    149 					case 1:	/* nfs server */
    150 					case 2:	/* gw */
    151 					case 3:	/* mask */
    152 					case 4:	/* host */
    153 						if (*s == 0)
    154 							break;
    155 						for (i = 0; i < 4; i++) {
    156 							b = strtoul(s, &s, 10);
    157 							xi_ip[ipidx] = b + 256
    158 								* xi_ip[ipidx];
    159 							if (*s != '.')
    160 								break;
    161 							s++;
    162 						}
    163 						if (i < 3)
    164 							xi_ip[ipidx] = 0;
    165 						break;
    166 					case 5:	/* interface */
    167 						if (!strncmp(s, "xennet", 6))
    168 							s += 6;
    169 						else if (!strncmp(s, "eth", 3))
    170 							s += 3;
    171 						else
    172 							break;
    173 						if (xcp->xcp_netinfo.xi_ifno
    174 						    == strtoul(s, NULL, 10))
    175 							memcpy(xcp->
    176 							    xcp_netinfo.xi_ip,
    177 							    xi_ip,
    178 							    sizeof(xi_ip));
    179 						break;
    180 					}
    181 					ipidx++;
    182 
    183 					if (opt)
    184 						*opt++ = ':';
    185 				}
    186 			}
    187 			break;
    188 
    189 		case XEN_PARSE_CONSOLE:
    190 			if (strncasecmp(opt, "console=", 8) == 0)
    191 				strncpy(xcp->xcp_console, opt + 8,
    192 				    sizeof(xcp->xcp_console));
    193 			break;
    194 
    195 		case XEN_PARSE_BOOTFLAGS:
    196 			if (*opt == '-') {
    197 				opt++;
    198 				while(*opt != '\0') {
    199 					BOOT_FLAG(*opt, boothowto);
    200 					opt++;
    201 				}
    202 			}
    203 			break;
    204 		case XEN_PARSE_PCIBACK:
    205 			if (strncasecmp(opt, "pciback.hide=", 13) == 0)
    206 				strncpy(xcp->xcp_pcidevs, opt + 13,
    207 				    sizeof(xcp->xcp_pcidevs));
    208 			break;
    209 		}
    210 
    211 		if (cmd_line)
    212 			*cmd_line++ = ' ';
    213 	}
    214 }
    215 
    216 u_int
    217 tsc_get_timecount(struct timecounter *tc)
    218 {
    219 
    220 	panic("xen: tsc_get_timecount");
    221 }
    222 
    223 /*
    224  * this function sets up the machdep.xen.suspend sysctl(7) that
    225  * controls domain suspend/save.
    226  */
    227 void
    228 sysctl_xen_suspend_setup(void)
    229 {
    230 	const struct sysctlnode *node = NULL;
    231 
    232 	/*
    233 	 * dom0 implements sleep support through ACPI. It should not call
    234 	 * this function to register a suspend interface.
    235 	 */
    236 	KASSERT(!(xendomain_is_dom0()));
    237 
    238 	sysctl_createv(NULL, 0, NULL, &node,
    239 	    CTLFLAG_PERMANENT,
    240 	    CTLTYPE_NODE, "machdep", NULL,
    241 	    NULL, 0, NULL, 0,
    242 	    CTL_MACHDEP, CTL_EOL);
    243 
    244 	sysctl_createv(NULL, 0, &node, &node,
    245 	    CTLFLAG_PERMANENT,
    246 	    CTLTYPE_NODE, "xen",
    247 	    SYSCTL_DESCR("Xen top level node"),
    248 	    NULL, 0, NULL, 0,
    249 	    CTL_CREATE, CTL_EOL);
    250 
    251 	sysctl_createv(NULL, 0, &node, &node,
    252 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE | CTLFLAG_IMMEDIATE,
    253 	    CTLTYPE_INT, "suspend",
    254 	    SYSCTL_DESCR("Suspend/save current Xen domain"),
    255 	    sysctl_xen_suspend, 0, NULL, 0,
    256 	    CTL_CREATE, CTL_EOL);
    257 }
    258 
    259 static int
    260 sysctl_xen_suspend(SYSCTLFN_ARGS)
    261 {
    262 	int error;
    263 	struct sysctlnode node;
    264 
    265 	node = *rnode;
    266 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    267 
    268 	if (error || newp == NULL)
    269 		return error;
    270 
    271 	/* only allow domain to suspend when dom0 instructed to do so */
    272 	if (xen_suspend_allow == false)
    273 		return EAGAIN;
    274 
    275 	xen_suspend_domain();
    276 
    277 	return 0;
    278 
    279 }
    280 
    281 /*
    282  * Last operations before suspending domain
    283  */
    284 static void
    285 xen_prepare_suspend(void)
    286 {
    287 
    288 	kpreempt_disable();
    289 
    290 	pmap_xen_suspend();
    291 	xen_suspendclocks(curcpu());
    292 
    293 	/*
    294 	 * save/restore code does not translate these MFNs to their
    295 	 * associated PFNs, so we must do it
    296 	 */
    297 	xen_start_info.store_mfn =
    298 	    atop(xpmap_mtop(ptoa(xen_start_info.store_mfn)));
    299 	xen_start_info.console_mfn =
    300 	    atop(xpmap_mtop(ptoa(xen_start_info.console_mfn)));
    301 
    302 	DPRINTK(("suspending domain\n"));
    303 	aprint_verbose("suspending domain\n");
    304 
    305 	/* invalidate the shared_info page */
    306 	if (HYPERVISOR_update_va_mapping((vaddr_t)HYPERVISOR_shared_info,
    307 	    0, UVMF_INVLPG)) {
    308 		DPRINTK(("HYPERVISOR_shared_info page invalidation failed"));
    309 		HYPERVISOR_crash();
    310 	}
    311 
    312 }
    313 
    314 /*
    315  * First operations before restoring domain context
    316  */
    317 static void
    318 xen_prepare_resume(void)
    319 {
    320 	/* map the new shared_info page */
    321 	if (HYPERVISOR_update_va_mapping((vaddr_t)HYPERVISOR_shared_info,
    322 	    xen_start_info.shared_info | PG_RW | PG_V,
    323 	    UVMF_INVLPG)) {
    324 		DPRINTK(("could not map new shared info page"));
    325 		HYPERVISOR_crash();
    326 	}
    327 
    328 	pmap_xen_resume();
    329 
    330 	if (xen_start_info.nr_pages != physmem) {
    331 		/*
    332 		 * XXX JYM for now, we crash - fix it with memory
    333 		 * hotplug when supported
    334 		 */
    335 		DPRINTK(("xen_start_info.nr_pages != physmem"));
    336 		HYPERVISOR_crash();
    337 	}
    338 
    339 	DPRINTK(("preparing domain resume\n"));
    340 	aprint_verbose("preparing domain resume\n");
    341 
    342 	xen_suspend_allow = false;
    343 
    344 	xen_resumeclocks(curcpu());
    345 
    346 	kpreempt_enable();
    347 
    348 }
    349 
    350 static void
    351 xen_suspend_domain(void)
    352 {
    353 	paddr_t mfn;
    354 	int s = splvm();
    355 
    356 	/*
    357 	 * console becomes unavailable when suspended, so
    358 	 * direct communications to domain are hampered from there on.
    359 	 * We can only rely on low level primitives like printk(), until
    360 	 * console is fully restored
    361 	 */
    362 	if (!pmf_system_suspend(PMF_Q_NONE)) {
    363 		DPRINTK(("devices suspend failed"));
    364 		HYPERVISOR_crash();
    365 	}
    366 
    367 	/*
    368 	 * obtain the MFN of the start_info page now, as we will not be
    369 	 * able to do it once pmap is locked
    370 	 */
    371 	pmap_extract_ma(pmap_kernel(), (vaddr_t)&xen_start_info, &mfn);
    372 	mfn >>= PAGE_SHIFT;
    373 
    374 	xen_prepare_suspend();
    375 
    376 	DPRINTK(("calling HYPERVISOR_suspend()\n"));
    377 	if (HYPERVISOR_suspend(mfn) != 0) {
    378 	/* XXX JYM: implement checkpoint/snapshot (ret == 1) */
    379 		DPRINTK(("HYPERVISOR_suspend() failed"));
    380 		HYPERVISOR_crash();
    381 	}
    382 
    383 	DPRINTK(("left HYPERVISOR_suspend()\n"));
    384 
    385 	xen_prepare_resume();
    386 
    387 	DPRINTK(("resuming devices\n"));
    388 	if (!pmf_system_resume(PMF_Q_NONE)) {
    389 		DPRINTK(("devices resume failed\n"));
    390 		HYPERVISOR_crash();
    391 	}
    392 
    393 	splx(s);
    394 
    395 	/* xencons is back online, we can print to console */
    396 	aprint_verbose("domain resumed\n");
    397 
    398 }
    399