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