1 /*- 2 * Copyright (c) 2022 Citrix Systems R&D 3 * Copyright (c) 2003-2005 Nate Lawson (SDG) 4 * Copyright (c) 2001 Michael Smith 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #include "opt_acpi.h" 31 #include <sys/param.h> 32 #include <sys/bus.h> 33 #include <sys/cpu.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 #include <sys/pcpu.h> 38 #include <sys/power.h> 39 #include <sys/proc.h> 40 #include <sys/sched.h> 41 42 #include <machine/_inttypes.h> 43 44 #include <contrib/dev/acpica/include/acpi.h> 45 #include <contrib/dev/acpica/include/accommon.h> 46 47 #include <dev/acpica/acpivar.h> 48 49 #include <xen/xen-os.h> 50 51 #define ACPI_DOMAIN_COORD_TYPE_SW_ALL 0xfc 52 #define ACPI_DOMAIN_COORD_TYPE_SW_ANY 0xfd 53 #define ACPI_DOMAIN_COORD_TYPE_HW_ALL 0xfe 54 55 #define ACPI_NOTIFY_PERF_STATES 0x80 /* _PSS changed. */ 56 #define ACPI_NOTIFY_CX_STATES 0x81 /* _CST changed. */ 57 58 static MALLOC_DEFINE(M_XENACPI, "xen_acpi", "Xen CPU ACPI forwarder"); 59 60 /* Hooks for the ACPI CA debugging infrastructure */ 61 #define _COMPONENT ACPI_PROCESSOR 62 ACPI_MODULE_NAME("PROCESSOR") 63 64 struct xen_acpi_cpu_softc { 65 device_t cpu_dev; 66 ACPI_HANDLE cpu_handle; 67 uint32_t cpu_acpi_id; 68 struct xen_processor_cx *cpu_cx_states; 69 unsigned int cpu_cx_count; 70 struct xen_processor_px *cpu_px_states; 71 unsigned int cpu_px_count; 72 struct xen_pct_register control_register; 73 struct xen_pct_register status_register; 74 struct xen_psd_package psd; 75 }; 76 77 #define CPUDEV_DEVICE_ID "ACPI0007" 78 79 ACPI_SERIAL_DECL(cpu, "ACPI CPU"); 80 81 #define device_printf(dev,...) \ 82 if (!device_is_quiet(dev)) \ 83 device_printf((dev), __VA_ARGS__) 84 85 static int 86 acpi_get_gas(const ACPI_OBJECT *res, unsigned int idx, 87 ACPI_GENERIC_ADDRESS *gas) 88 { 89 const ACPI_OBJECT *obj = &res->Package.Elements[idx]; 90 91 if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER || 92 obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3) 93 return (EINVAL); 94 95 memcpy(gas, obj->Buffer.Pointer + 3, sizeof(*gas)); 96 97 return (0); 98 } 99 100 static int 101 acpi_get_pct(const ACPI_OBJECT *res, unsigned int idx, 102 struct xen_pct_register *reg) 103 { 104 struct { 105 uint8_t descriptor; 106 uint16_t length; 107 ACPI_GENERIC_ADDRESS gas; 108 } __packed raw; 109 const ACPI_OBJECT *obj = &res->Package.Elements[idx]; 110 111 if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER || 112 obj->Buffer.Length < sizeof(raw)) 113 return (EINVAL); 114 115 memcpy(&raw, obj->Buffer.Pointer, sizeof(raw)); 116 reg->descriptor = raw.descriptor; 117 reg->length = raw.length; 118 reg->space_id = raw.gas.SpaceId; 119 reg->bit_width = raw.gas.BitWidth; 120 reg->bit_offset = raw.gas.BitOffset; 121 reg->reserved = raw.gas.AccessWidth; 122 reg->address = raw.gas.Address; 123 124 return (0); 125 } 126 127 static int 128 xen_upload_cx(struct xen_acpi_cpu_softc *sc) 129 { 130 struct xen_platform_op op = { 131 .cmd = XENPF_set_processor_pminfo, 132 .interface_version = XENPF_INTERFACE_VERSION, 133 .u.set_pminfo.id = sc->cpu_acpi_id, 134 .u.set_pminfo.type = XEN_PM_CX, 135 .u.set_pminfo.u.power.count = sc->cpu_cx_count, 136 .u.set_pminfo.u.power.flags.has_cst = 1, 137 /* Ignore bm_check and bm_control, Xen will set those. */ 138 }; 139 int error; 140 141 set_xen_guest_handle(op.u.set_pminfo.u.power.states, sc->cpu_cx_states); 142 143 error = HYPERVISOR_platform_op(&op); 144 if (error != 0) 145 device_printf(sc->cpu_dev, 146 "ACPI ID %u Cx upload failed: %d\n", sc->cpu_acpi_id, 147 error); 148 return (error); 149 } 150 151 static int 152 xen_upload_px(struct xen_acpi_cpu_softc *sc) 153 { 154 struct xen_platform_op op = { 155 .cmd = XENPF_set_processor_pminfo, 156 .interface_version = XENPF_INTERFACE_VERSION, 157 .u.set_pminfo.id = sc->cpu_acpi_id, 158 .u.set_pminfo.type = XEN_PM_PX, 159 .u.set_pminfo.u.perf.state_count = sc->cpu_px_count, 160 .u.set_pminfo.u.perf.control_register = sc->control_register, 161 .u.set_pminfo.u.perf.status_register = sc->status_register, 162 .u.set_pminfo.u.perf.domain_info = sc->psd, 163 .u.set_pminfo.u.perf.flags = XEN_PX_PPC | XEN_PX_PCT | 164 XEN_PX_PSS | XEN_PX_PSD, 165 }; 166 ACPI_STATUS status; 167 int error; 168 169 status = acpi_GetInteger(sc->cpu_handle, "_PPC", 170 &op.u.set_pminfo.u.perf.platform_limit); 171 if (ACPI_FAILURE(status)) { 172 device_printf(sc->cpu_dev, "missing _PPC object\n"); 173 return (ENXIO); 174 } 175 176 set_xen_guest_handle(op.u.set_pminfo.u.perf.states, sc->cpu_px_states); 177 178 /* 179 * NB: it's unclear the exact purpose of the shared_type field, or why 180 * it can't be calculated by Xen itself. Naively set it here to allow 181 * the upload to succeed. 182 */ 183 switch (sc->psd.coord_type) { 184 case ACPI_DOMAIN_COORD_TYPE_SW_ALL: 185 op.u.set_pminfo.u.perf.shared_type = 186 XEN_CPUPERF_SHARED_TYPE_ALL; 187 break; 188 189 case ACPI_DOMAIN_COORD_TYPE_HW_ALL: 190 op.u.set_pminfo.u.perf.shared_type = 191 XEN_CPUPERF_SHARED_TYPE_HW; 192 break; 193 194 case ACPI_DOMAIN_COORD_TYPE_SW_ANY: 195 op.u.set_pminfo.u.perf.shared_type = 196 XEN_CPUPERF_SHARED_TYPE_ANY; 197 break; 198 default: 199 device_printf(sc->cpu_dev, 200 "unknown coordination type %#" PRIx64 "\n", 201 sc->psd.coord_type); 202 return (EINVAL); 203 } 204 205 error = HYPERVISOR_platform_op(&op); 206 if (error != 0) 207 device_printf(sc->cpu_dev, 208 "ACPI ID %u Px upload failed: %d\n", sc->cpu_acpi_id, error); 209 return (error); 210 } 211 212 static int 213 acpi_set_pdc(const struct xen_acpi_cpu_softc *sc) 214 { 215 struct xen_platform_op op = { 216 .cmd = XENPF_set_processor_pminfo, 217 .interface_version = XENPF_INTERFACE_VERSION, 218 .u.set_pminfo.id = -1, 219 .u.set_pminfo.type = XEN_PM_PDC, 220 }; 221 uint32_t pdc[3] = {1, 1}; 222 ACPI_OBJECT arg = { 223 .Buffer.Type = ACPI_TYPE_BUFFER, 224 .Buffer.Length = sizeof(pdc), 225 .Buffer.Pointer = (uint8_t *)pdc, 226 }; 227 ACPI_OBJECT_LIST arglist = { 228 .Pointer = &arg, 229 .Count = 1, 230 }; 231 ACPI_STATUS status; 232 int error; 233 234 set_xen_guest_handle(op.u.set_pminfo.u.pdc, pdc); 235 error = HYPERVISOR_platform_op(&op); 236 if (error != 0) { 237 device_printf(sc->cpu_dev, 238 "unable to get _PDC features from Xen: %d\n", error); 239 return (error); 240 } 241 242 status = AcpiEvaluateObject(sc->cpu_handle, "_PDC", &arglist, NULL); 243 if (ACPI_FAILURE(status)) { 244 device_printf(sc->cpu_dev, "unable to execute _PDC - %s\n", 245 AcpiFormatException(status)); 246 return (ENXIO); 247 } 248 249 return (0); 250 } 251 252 /* 253 * Parse a _CST package and set up its Cx states. Since the _CST object 254 * can change dynamically, our notify handler may call this function 255 * to clean up and probe the new _CST package. 256 */ 257 static int 258 acpi_fetch_cx(struct xen_acpi_cpu_softc *sc) 259 { 260 ACPI_STATUS status; 261 ACPI_BUFFER buf = { 262 .Length = ACPI_ALLOCATE_BUFFER, 263 }; 264 ACPI_OBJECT *top; 265 uint32_t count; 266 unsigned int i; 267 268 status = AcpiEvaluateObject(sc->cpu_handle, "_CST", NULL, &buf); 269 if (ACPI_FAILURE(status)) { 270 device_printf(sc->cpu_dev, "missing _CST object\n"); 271 return (ENXIO); 272 } 273 274 /* _CST is a package with a count and at least one Cx package. */ 275 top = (ACPI_OBJECT *)buf.Pointer; 276 if (!ACPI_PKG_VALID(top, 2) || acpi_PkgInt32(top, 0, &count) != 0) { 277 device_printf(sc->cpu_dev, "invalid _CST package\n"); 278 AcpiOsFree(buf.Pointer); 279 return (ENXIO); 280 } 281 if (count != top->Package.Count - 1) { 282 device_printf(sc->cpu_dev, 283 "invalid _CST state count (%u != %u)\n", 284 count, top->Package.Count - 1); 285 count = top->Package.Count - 1; 286 } 287 288 sc->cpu_cx_states = mallocarray(count, sizeof(struct xen_processor_cx), 289 M_XENACPI, M_WAITOK | M_ZERO); 290 291 sc->cpu_cx_count = 0; 292 for (i = 0; i < count; i++) { 293 uint32_t type; 294 ACPI_GENERIC_ADDRESS gas; 295 ACPI_OBJECT *pkg = &top->Package.Elements[i + 1]; 296 struct xen_processor_cx *cx_ptr = 297 &sc->cpu_cx_states[sc->cpu_cx_count]; 298 299 if (!ACPI_PKG_VALID(pkg, 4) || 300 acpi_PkgInt32(pkg, 1, &type) != 0 || 301 acpi_PkgInt32(pkg, 2, &cx_ptr->latency) != 0 || 302 acpi_PkgInt32(pkg, 3, &cx_ptr->power) != 0 || 303 acpi_get_gas(pkg, 0, &gas) != 0) { 304 device_printf(sc->cpu_dev, 305 "skipping invalid _CST %u package\n", 306 i + 1); 307 continue; 308 } 309 310 cx_ptr->type = type; 311 cx_ptr->reg.space_id = gas.SpaceId; 312 cx_ptr->reg.bit_width = gas.BitWidth; 313 cx_ptr->reg.bit_offset = gas.BitOffset; 314 cx_ptr->reg.access_size = gas.AccessWidth; 315 cx_ptr->reg.address = gas.Address; 316 sc->cpu_cx_count++; 317 } 318 AcpiOsFree(buf.Pointer); 319 320 if (sc->cpu_cx_count == 0) { 321 device_printf(sc->cpu_dev, "no valid _CST package found\n"); 322 free(sc->cpu_cx_states, M_XENACPI); 323 sc->cpu_cx_states = NULL; 324 return (ENXIO); 325 } 326 327 return (0); 328 } 329 330 /* Probe and setup any valid performance states (Px). */ 331 static int 332 acpi_fetch_px(struct xen_acpi_cpu_softc *sc) 333 { 334 ACPI_BUFFER buf = { 335 .Length = ACPI_ALLOCATE_BUFFER, 336 }; 337 ACPI_OBJECT *pkg, *res; 338 ACPI_STATUS status; 339 unsigned int count, i; 340 int error; 341 uint64_t *p; 342 343 /* _PSS */ 344 status = AcpiEvaluateObject(sc->cpu_handle, "_PSS", NULL, &buf); 345 if (ACPI_FAILURE(status)) { 346 device_printf(sc->cpu_dev, "missing _PSS object\n"); 347 return (ENXIO); 348 } 349 350 pkg = (ACPI_OBJECT *)buf.Pointer; 351 if (!ACPI_PKG_VALID(pkg, 1)) { 352 device_printf(sc->cpu_dev, "invalid top level _PSS package\n"); 353 goto error; 354 } 355 count = pkg->Package.Count; 356 357 sc->cpu_px_states = mallocarray(count, sizeof(struct xen_processor_px), 358 M_XENACPI, M_WAITOK | M_ZERO); 359 360 /* 361 * Each state is a package of {CoreFreq, Power, TransitionLatency, 362 * BusMasterLatency, ControlVal, StatusVal}, sorted from highest 363 * performance to lowest. 364 */ 365 sc->cpu_px_count = 0; 366 for (i = 0; i < count; i++) { 367 unsigned int j; 368 369 res = &pkg->Package.Elements[i]; 370 if (!ACPI_PKG_VALID(res, 6)) { 371 device_printf(sc->cpu_dev, 372 "invalid _PSS package idx %u\n", i); 373 continue; 374 } 375 376 /* Parse the rest of the package into the struct. */ 377 p = (uint64_t *)&sc->cpu_px_states[sc->cpu_px_count++]; 378 for (j = 0; j < 6; j++, p++) 379 acpi_PkgInt(res, j, p); 380 } 381 382 /* No valid Px state found so give up. */ 383 if (sc->cpu_px_count == 0) { 384 device_printf(sc->cpu_dev, "no valid _PSS package found\n"); 385 goto error; 386 } 387 AcpiOsFree(buf.Pointer); 388 389 /* _PCT */ 390 buf.Pointer = NULL; 391 buf.Length = ACPI_ALLOCATE_BUFFER; 392 status = AcpiEvaluateObject(sc->cpu_handle, "_PCT", NULL, &buf); 393 if (ACPI_FAILURE(status)) { 394 device_printf(sc->cpu_dev, "missing _PCT object\n"); 395 goto error; 396 } 397 398 /* Check the package of two registers, each a Buffer in GAS format. */ 399 pkg = (ACPI_OBJECT *)buf.Pointer; 400 if (!ACPI_PKG_VALID(pkg, 2)) { 401 device_printf(sc->cpu_dev, "invalid top level _PCT package\n"); 402 goto error; 403 } 404 405 error = acpi_get_pct(pkg, 0, &sc->control_register); 406 if (error != 0) { 407 device_printf(sc->cpu_dev, 408 "unable to fetch _PCT control register: %d\n", error); 409 goto error; 410 } 411 error = acpi_get_pct(pkg, 1, &sc->status_register); 412 if (error != 0) { 413 device_printf(sc->cpu_dev, 414 "unable to fetch _PCT status register: %d\n", error); 415 goto error; 416 } 417 AcpiOsFree(buf.Pointer); 418 419 /* _PSD */ 420 buf.Pointer = NULL; 421 buf.Length = ACPI_ALLOCATE_BUFFER; 422 status = AcpiEvaluateObject(sc->cpu_handle, "_PSD", NULL, &buf); 423 if (ACPI_FAILURE(status)) { 424 device_printf(sc->cpu_dev, "missing _PSD object\n"); 425 goto error; 426 } 427 428 pkg = (ACPI_OBJECT *)buf.Pointer; 429 if (!ACPI_PKG_VALID(pkg, 1)) { 430 device_printf(sc->cpu_dev, "invalid top level _PSD package\n"); 431 goto error; 432 } 433 434 res = &pkg->Package.Elements[0]; 435 if (!ACPI_PKG_VALID(res, 5)) { 436 printf("invalid _PSD package\n"); 437 goto error; 438 } 439 440 p = (uint64_t *)&sc->psd; 441 for (i = 0; i < 5; i++, p++) 442 acpi_PkgInt(res, i, p); 443 AcpiOsFree(buf.Pointer); 444 445 return (0); 446 447 error: 448 if (buf.Pointer != NULL) 449 AcpiOsFree(buf.Pointer); 450 if (sc->cpu_px_states != NULL) { 451 free(sc->cpu_px_states, M_XENACPI); 452 sc->cpu_px_states = NULL; 453 } 454 return (ENXIO); 455 } 456 457 static void 458 acpi_notify(ACPI_HANDLE h, UINT32 notify, void *context) 459 { 460 struct xen_acpi_cpu_softc *sc = context; 461 462 switch (notify) { 463 case ACPI_NOTIFY_PERF_STATES: 464 if (acpi_fetch_px(sc) != 0) 465 break; 466 xen_upload_px(sc); 467 free(sc->cpu_px_states, M_XENACPI); 468 sc->cpu_px_states = NULL; 469 break; 470 471 case ACPI_NOTIFY_CX_STATES: 472 if (acpi_fetch_cx(sc) != 0) 473 break; 474 xen_upload_cx(sc); 475 free(sc->cpu_cx_states, M_XENACPI); 476 sc->cpu_cx_states = NULL; 477 break; 478 } 479 } 480 481 static int 482 xen_acpi_cpu_probe(device_t dev) 483 { 484 static char *cpudev_ids[] = { CPUDEV_DEVICE_ID, NULL }; 485 ACPI_OBJECT_TYPE type = acpi_get_type(dev); 486 487 if (!xen_initial_domain()) 488 return (ENXIO); 489 if (type != ACPI_TYPE_PROCESSOR && type != ACPI_TYPE_DEVICE) 490 return (ENXIO); 491 if (type == ACPI_TYPE_DEVICE && 492 ACPI_ID_PROBE(device_get_parent(dev), dev, cpudev_ids, NULL) >= 0) 493 return (ENXIO); 494 495 device_set_desc(dev, "XEN ACPI CPU"); 496 if (!bootverbose) 497 device_quiet(dev); 498 499 /* 500 * Use SPECIFIC because when running as a Xen dom0 the ACPI PROCESSOR 501 * data is the native one, and needs to be forwarded to Xen but not 502 * used by FreeBSD itself. 503 */ 504 return (BUS_PROBE_SPECIFIC); 505 } 506 507 static bool 508 is_processor_online(unsigned int acpi_id) 509 { 510 unsigned int i, maxid; 511 struct xen_platform_op op = { 512 .cmd = XENPF_get_cpuinfo, 513 }; 514 int ret = HYPERVISOR_platform_op(&op); 515 516 if (ret) 517 return (false); 518 519 maxid = op.u.pcpu_info.max_present; 520 for (i = 0; i <= maxid; i++) { 521 op.u.pcpu_info.xen_cpuid = i; 522 ret = HYPERVISOR_platform_op(&op); 523 if (ret) 524 continue; 525 if (op.u.pcpu_info.acpi_id == acpi_id) 526 return (op.u.pcpu_info.flags & XEN_PCPU_FLAGS_ONLINE); 527 } 528 529 return (false); 530 } 531 532 static int 533 xen_acpi_cpu_attach(device_t dev) 534 { 535 struct xen_acpi_cpu_softc *sc = device_get_softc(dev); 536 ACPI_STATUS status; 537 int error; 538 539 sc->cpu_dev = dev; 540 sc->cpu_handle = acpi_get_handle(dev); 541 542 if (acpi_get_type(dev) == ACPI_TYPE_PROCESSOR) { 543 ACPI_BUFFER buf = { 544 .Length = ACPI_ALLOCATE_BUFFER, 545 }; 546 ACPI_OBJECT *obj; 547 548 status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf); 549 if (ACPI_FAILURE(status)) { 550 device_printf(dev, 551 "attach failed to get Processor obj - %s\n", 552 AcpiFormatException(status)); 553 return (ENXIO); 554 } 555 obj = (ACPI_OBJECT *)buf.Pointer; 556 sc->cpu_acpi_id = obj->Processor.ProcId; 557 AcpiOsFree(obj); 558 } else { 559 KASSERT(acpi_get_type(dev) == ACPI_TYPE_DEVICE, 560 ("Unexpected ACPI object")); 561 status = acpi_GetInteger(sc->cpu_handle, "_UID", 562 &sc->cpu_acpi_id); 563 if (ACPI_FAILURE(status)) { 564 device_printf(dev, "device object has bad value - %s\n", 565 AcpiFormatException(status)); 566 return (ENXIO); 567 } 568 } 569 570 if (!is_processor_online(sc->cpu_acpi_id)) 571 /* Processor is not online, attach the driver and ignore it. */ 572 return (0); 573 574 /* 575 * Install the notify handler now: even if we fail to parse or upload 576 * the states it shouldn't prevent us from attempting to parse further 577 * updates. 578 */ 579 status = AcpiInstallNotifyHandler(sc->cpu_handle, ACPI_DEVICE_NOTIFY, 580 acpi_notify, sc); 581 if (ACPI_FAILURE(status)) 582 device_printf(dev, "failed to register notify handler - %s\n", 583 AcpiFormatException(status)); 584 585 /* 586 * Don't report errors: it's likely there are processor objects 587 * belonging to CPUs that are not online, but the MADT provided to 588 * FreeBSD is crafted to report the number of CPUs available to dom0. 589 * 590 * Parsing or uploading those states could result in errors, just 591 * ignore them in order to avoid pointless noise. 592 */ 593 error = acpi_set_pdc(sc); 594 if (error != 0) 595 return (0); 596 597 error = acpi_fetch_px(sc); 598 if (error != 0) 599 return (0); 600 error = xen_upload_px(sc); 601 free(sc->cpu_px_states, M_XENACPI); 602 sc->cpu_px_states = NULL; 603 if (error != 0) 604 return (0); 605 606 error = acpi_fetch_cx(sc); 607 if (error != 0) 608 return (0); 609 xen_upload_cx(sc); 610 free(sc->cpu_cx_states, M_XENACPI); 611 sc->cpu_cx_states = NULL; 612 613 return (0); 614 } 615 616 static device_method_t xen_acpi_cpu_methods[] = { 617 /* Device interface */ 618 DEVMETHOD(device_probe, xen_acpi_cpu_probe), 619 DEVMETHOD(device_attach, xen_acpi_cpu_attach), 620 621 DEVMETHOD_END 622 }; 623 624 static driver_t xen_acpi_cpu_driver = { 625 "xen cpu", 626 xen_acpi_cpu_methods, 627 sizeof(struct xen_acpi_cpu_softc), 628 }; 629 630 DRIVER_MODULE(xen_cpu, acpi, xen_acpi_cpu_driver, 0, 0); 631 MODULE_DEPEND(xen_cpu, acpi, 1, 1, 1); 632