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