1/*- 2 * Copyright (c) 2013 Hudson River Trading LLC 3 * Written by: John H. Baldwin <jhb@FreeBSD.org> 4 * All rights reserved. 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 AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/pm.c 281887 2015-04-23 14:22:20Z jhb $"); 30 31#include <sys/types.h> 32#include <machine/vmm.h> 33 34#include <assert.h> 35#include <errno.h> 36#include <pthread.h> 37#include <signal.h> 38#include <vmmapi.h> 39 40#include "acpi.h" 41#include "inout.h" 42#include "mevent.h" 43#include "pci_irq.h" 44#include "pci_lpc.h" 45 46static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER; 47static struct mevent *power_button; 48static sig_t old_power_handler; 49 50/* 51 * Reset Control register at I/O port 0xcf9. Bit 2 forces a system 52 * reset when it transitions from 0 to 1. Bit 1 selects the type of 53 * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard" 54 * reset. 55 */ 56static int 57reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 58 uint32_t *eax, void *arg) 59{ 60 int error; 61 62 static uint8_t reset_control; 63 64 if (bytes != 1) 65 return (-1); 66 if (in) 67 *eax = reset_control; 68 else { 69 reset_control = *eax; 70 71 /* Treat hard and soft resets the same. */ 72 if (reset_control & 0x4) { 73 error = vm_suspend(ctx, VM_SUSPEND_RESET); 74 assert(error == 0 || errno == EALREADY); 75 } 76 } 77 return (0); 78} 79INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler); 80 81/* 82 * ACPI's SCI is a level-triggered interrupt. 83 */ 84static int sci_active; 85 86static void 87sci_assert(struct vmctx *ctx) 88{ 89 90 if (sci_active) 91 return; 92 vm_isa_assert_irq(ctx, SCI_INT, SCI_INT); 93 sci_active = 1; 94} 95 96static void 97sci_deassert(struct vmctx *ctx) 98{ 99 100 if (!sci_active) 101 return; 102 vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT); 103 sci_active = 0; 104} 105 106/* 107 * Power Management 1 Event Registers 108 * 109 * The only power management event supported is a power button upon 110 * receiving SIGTERM. 111 */ 112static uint16_t pm1_enable, pm1_status; 113 114#define PM1_TMR_STS 0x0001 115#define PM1_BM_STS 0x0010 116#define PM1_GBL_STS 0x0020 117#define PM1_PWRBTN_STS 0x0100 118#define PM1_SLPBTN_STS 0x0200 119#define PM1_RTC_STS 0x0400 120#define PM1_WAK_STS 0x8000 121 122#define PM1_TMR_EN 0x0001 123#define PM1_GBL_EN 0x0020 124#define PM1_PWRBTN_EN 0x0100 125#define PM1_SLPBTN_EN 0x0200 126#define PM1_RTC_EN 0x0400 127 128static void 129sci_update(struct vmctx *ctx) 130{ 131 int need_sci; 132 133 /* See if the SCI should be active or not. */ 134 need_sci = 0; 135 if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS)) 136 need_sci = 1; 137 if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS)) 138 need_sci = 1; 139 if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS)) 140 need_sci = 1; 141 if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS)) 142 need_sci = 1; 143 if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS)) 144 need_sci = 1; 145 if (need_sci) 146 sci_assert(ctx); 147 else 148 sci_deassert(ctx); 149} 150 151static int 152pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 153 uint32_t *eax, void *arg) 154{ 155 156 if (bytes != 2) 157 return (-1); 158 159 pthread_mutex_lock(&pm_lock); 160 if (in) 161 *eax = pm1_status; 162 else { 163 /* 164 * Writes are only permitted to clear certain bits by 165 * writing 1 to those flags. 166 */ 167 pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS | 168 PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS)); 169 sci_update(ctx); 170 } 171 pthread_mutex_unlock(&pm_lock); 172 return (0); 173} 174 175static int 176pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 177 uint32_t *eax, void *arg) 178{ 179 180 if (bytes != 2) 181 return (-1); 182 183 pthread_mutex_lock(&pm_lock); 184 if (in) 185 *eax = pm1_enable; 186 else { 187 /* 188 * Only permit certain bits to be set. We never use 189 * the global lock, but ACPI-CA whines profusely if it 190 * can't set GBL_EN. 191 */ 192 pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN); 193 sci_update(ctx); 194 } 195 pthread_mutex_unlock(&pm_lock); 196 return (0); 197} 198INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler); 199INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler); 200 201static void 202power_button_handler(int signal, enum ev_type type, void *arg) 203{ 204 struct vmctx *ctx; 205 206 ctx = arg; 207 pthread_mutex_lock(&pm_lock); 208 if (!(pm1_status & PM1_PWRBTN_STS)) { 209 pm1_status |= PM1_PWRBTN_STS; 210 sci_update(ctx); 211 } 212 pthread_mutex_unlock(&pm_lock); 213} 214 215/* 216 * Power Management 1 Control Register 217 * 218 * This is mostly unimplemented except that we wish to handle writes that 219 * set SPL_EN to handle S5 (soft power off). 220 */ 221static uint16_t pm1_control; 222 223#define PM1_SCI_EN 0x0001 224#define PM1_SLP_TYP 0x1c00 225#define PM1_SLP_EN 0x2000 226#define PM1_ALWAYS_ZERO 0xc003 227 228static int 229pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 230 uint32_t *eax, void *arg) 231{ 232 int error; 233 234 if (bytes != 2) 235 return (-1); 236 if (in) 237 *eax = pm1_control; 238 else { 239 /* 240 * Various bits are write-only or reserved, so force them 241 * to zero in pm1_control. Always preserve SCI_EN as OSPM 242 * can never change it. 243 */ 244 pm1_control = (pm1_control & PM1_SCI_EN) | 245 (*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO)); 246 247 /* 248 * If SLP_EN is set, check for S5. Bhyve's _S5_ method 249 * says that '5' should be stored in SLP_TYP for S5. 250 */ 251 if (*eax & PM1_SLP_EN) { 252 if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) { 253 error = vm_suspend(ctx, VM_SUSPEND_POWEROFF); 254 assert(error == 0 || errno == EALREADY); 255 } 256 } 257 } 258 return (0); 259} 260INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler); 261SYSRES_IO(PM1A_EVT_ADDR, 8); 262 263/* 264 * ACPI SMI Command Register 265 * 266 * This write-only register is used to enable and disable ACPI. 267 */ 268static int 269smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 270 uint32_t *eax, void *arg) 271{ 272 273 assert(!in); 274 if (bytes != 1) 275 return (-1); 276 277 pthread_mutex_lock(&pm_lock); 278 switch (*eax) { 279 case BHYVE_ACPI_ENABLE: 280 pm1_control |= PM1_SCI_EN; 281 if (power_button == NULL) { 282 power_button = mevent_add(SIGTERM, EVF_SIGNAL, 283 power_button_handler, ctx); 284 old_power_handler = signal(SIGTERM, SIG_IGN); 285 } 286 break; 287 case BHYVE_ACPI_DISABLE: 288 pm1_control &= ~PM1_SCI_EN; 289 if (power_button != NULL) { 290 mevent_delete(power_button); 291 power_button = NULL; 292 signal(SIGTERM, old_power_handler); 293 } 294 break; 295 } 296 pthread_mutex_unlock(&pm_lock); 297 return (0); 298} 299INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler); 300SYSRES_IO(SMI_CMD, 1); 301 302void 303sci_init(struct vmctx *ctx) 304{ 305 306 /* 307 * Mark ACPI's SCI as level trigger and bump its use count 308 * in the PIRQ router. 309 */ 310 pci_irq_use(SCI_INT); 311 vm_isa_set_irq_trigger(ctx, SCI_INT, LEVEL_TRIGGER); 312} 313