pm.c revision 268972
1139749Simp/*- 2136944Syongari * Copyright (c) 2013 Advanced Computing Technologies LLC 3136944Syongari * Written by: John H. Baldwin <jhb@FreeBSD.org> 4136944Syongari * All rights reserved. 5136944Syongari * 6136944Syongari * Redistribution and use in source and binary forms, with or without 7136944Syongari * modification, are permitted provided that the following conditions 8136944Syongari * are met: 9136944Syongari * 1. Redistributions of source code must retain the above copyright 10136944Syongari * notice, this list of conditions and the following disclaimer. 11136944Syongari * 2. Redistributions in binary form must reproduce the above copyright 12136944Syongari * notice, this list of conditions and the following disclaimer in the 13136944Syongari * documentation and/or other materials provided with the distribution. 14136944Syongari * 15136944Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16136944Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17136944Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18136944Syongari * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19136944Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20136944Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21136944Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22136944Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23136944Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24136944Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25136944Syongari * SUCH DAMAGE. 26136944Syongari */ 27136944Syongari 28136944Syongari#include <sys/cdefs.h> 29136944Syongari__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/pm.c 268972 2014-07-22 03:14:37Z jhb $"); 30136944Syongari 31136944Syongari#include <sys/types.h> 32136944Syongari#include <machine/vmm.h> 33136944Syongari 34136944Syongari#include <assert.h> 35136944Syongari#include <pthread.h> 36136944Syongari#include <signal.h> 37136944Syongari#include <vmmapi.h> 38136944Syongari 39136944Syongari#include "acpi.h" 40136944Syongari#include "inout.h" 41136944Syongari#include "mevent.h" 42136944Syongari#include "pci_irq.h" 43136944Syongari#include "pci_lpc.h" 44136944Syongari 45136944Syongaristatic pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER; 46136944Syongaristatic struct mevent *power_button; 47136944Syongaristatic sig_t old_power_handler; 48136944Syongari 49136944Syongari/* 50136944Syongari * Reset Control register at I/O port 0xcf9. Bit 2 forces a system 51136944Syongari * reset when it transitions from 0 to 1. Bit 1 selects the type of 52136944Syongari * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard" 53193640Sariff * reset. 54193640Sariff */ 55193640Sariffstatic int 56193640Sariffreset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 57136944Syongari uint32_t *eax, void *arg) 58136944Syongari{ 59136944Syongari static uint8_t reset_control; 60136944Syongari 61136944Syongari if (bytes != 1) 62136944Syongari return (-1); 63136944Syongari if (in) 64136944Syongari *eax = reset_control; 65136944Syongari else { 66136944Syongari reset_control = *eax; 67136944Syongari 68136944Syongari /* Treat hard and soft resets the same. */ 69136944Syongari if (reset_control & 0x4) 70136944Syongari return (INOUT_RESET); 71136944Syongari } 72136944Syongari return (0); 73215034Sbrucec} 74136944SyongariINOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler); 75136944Syongari 76136944Syongari/* 77136944Syongari * ACPI's SCI is a level-triggered interrupt. 78136944Syongari */ 79136944Syongaristatic int sci_active; 80136944Syongari 81136944Syongaristatic void 82136944Syongarisci_assert(struct vmctx *ctx) 83136944Syongari{ 84136944Syongari 85136944Syongari if (sci_active) 86136944Syongari return; 87136944Syongari vm_isa_assert_irq(ctx, SCI_INT, SCI_INT); 88136944Syongari sci_active = 1; 89136944Syongari} 90136944Syongari 91136944Syongaristatic void 92136944Syongarisci_deassert(struct vmctx *ctx) 93136944Syongari{ 94136944Syongari 95136944Syongari if (!sci_active) 96136944Syongari return; 97136944Syongari vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT); 98136944Syongari sci_active = 0; 99136944Syongari} 100136944Syongari 101136944Syongari/* 102136944Syongari * Power Management 1 Event Registers 103136944Syongari * 104136944Syongari * The only power management event supported is a power button upon 105136944Syongari * receiving SIGTERM. 106136944Syongari */ 107136944Syongaristatic uint16_t pm1_enable, pm1_status; 108136944Syongari 109136944Syongari#define PM1_TMR_STS 0x0001 110136944Syongari#define PM1_BM_STS 0x0010 111136944Syongari#define PM1_GBL_STS 0x0020 112136944Syongari#define PM1_PWRBTN_STS 0x0100 113136944Syongari#define PM1_SLPBTN_STS 0x0200 114136944Syongari#define PM1_RTC_STS 0x0400 115136944Syongari#define PM1_WAK_STS 0x8000 116136944Syongari 117136944Syongari#define PM1_TMR_EN 0x0001 118136944Syongari#define PM1_GBL_EN 0x0020 119136944Syongari#define PM1_PWRBTN_EN 0x0100 120136944Syongari#define PM1_SLPBTN_EN 0x0200 121136944Syongari#define PM1_RTC_EN 0x0400 122136944Syongari 123136944Syongaristatic void 124136944Syongarisci_update(struct vmctx *ctx) 125136944Syongari{ 126136944Syongari int need_sci; 127136944Syongari 128136944Syongari /* See if the SCI should be active or not. */ 129136944Syongari need_sci = 0; 130136944Syongari if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS)) 131136944Syongari need_sci = 1; 132136944Syongari if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS)) 133136944Syongari need_sci = 1; 134136944Syongari if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS)) 135136944Syongari need_sci = 1; 136136944Syongari if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS)) 137136944Syongari need_sci = 1; 138136944Syongari if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS)) 139136944Syongari need_sci = 1; 140136944Syongari if (need_sci) 141136944Syongari sci_assert(ctx); 142136944Syongari else 143136944Syongari sci_deassert(ctx); 144136944Syongari} 145136944Syongari 146136944Syongaristatic int 147136944Syongaripm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 148136944Syongari uint32_t *eax, void *arg) 149136944Syongari{ 150136944Syongari 151136944Syongari if (bytes != 2) 152136944Syongari return (-1); 153136944Syongari 154136944Syongari pthread_mutex_lock(&pm_lock); 155136944Syongari if (in) 156136944Syongari *eax = pm1_status; 157136944Syongari else { 158136944Syongari /* 159136944Syongari * Writes are only permitted to clear certain bits by 160136944Syongari * writing 1 to those flags. 161136944Syongari */ 162136944Syongari pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS | 163136944Syongari PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS)); 164136944Syongari sci_update(ctx); 165136944Syongari } 166136944Syongari pthread_mutex_unlock(&pm_lock); 167136944Syongari return (0); 168136944Syongari} 169136944Syongari 170136944Syongaristatic int 171136944Syongaripm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 172136944Syongari uint32_t *eax, void *arg) 173136944Syongari{ 174136944Syongari 175136944Syongari if (bytes != 2) 176193779Sariff return (-1); 177136944Syongari 178136944Syongari pthread_mutex_lock(&pm_lock); 179136944Syongari if (in) 180193779Sariff *eax = pm1_enable; 181136944Syongari else { 182193779Sariff /* 183136944Syongari * Only permit certain bits to be set. We never use 184193779Sariff * the global lock, but ACPI-CA whines profusely if it 185136944Syongari * can't set GBL_EN. 186136944Syongari */ 187136944Syongari pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN); 188136944Syongari sci_update(ctx); 189136944Syongari } 190136944Syongari pthread_mutex_unlock(&pm_lock); 191136944Syongari return (0); 192136944Syongari} 193136944SyongariINOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler); 194136944SyongariINOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler); 195136944Syongari 196136944Syongaristatic void 197136944Syongaripower_button_handler(int signal, enum ev_type type, void *arg) 198136944Syongari{ 199136944Syongari struct vmctx *ctx; 200136944Syongari 201136944Syongari ctx = arg; 202136944Syongari pthread_mutex_lock(&pm_lock); 203136944Syongari if (!(pm1_status & PM1_PWRBTN_STS)) { 204136944Syongari pm1_status |= PM1_PWRBTN_STS; 205136944Syongari sci_update(ctx); 206136944Syongari } 207136944Syongari pthread_mutex_unlock(&pm_lock); 208136944Syongari} 209136944Syongari 210136944Syongari/* 211136944Syongari * Power Management 1 Control Register 212136944Syongari * 213136944Syongari * This is mostly unimplemented except that we wish to handle writes that 214136944Syongari * set SPL_EN to handle S5 (soft power off). 215136944Syongari */ 216136944Syongaristatic uint16_t pm1_control; 217136944Syongari 218136944Syongari#define PM1_SCI_EN 0x0001 219136944Syongari#define PM1_SLP_TYP 0x1c00 220136944Syongari#define PM1_SLP_EN 0x2000 221136944Syongari#define PM1_ALWAYS_ZERO 0xc003 222136944Syongari 223136944Syongaristatic int 224136944Syongaripm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 225136944Syongari uint32_t *eax, void *arg) 226136944Syongari{ 227136944Syongari 228136944Syongari if (bytes != 2) 229136944Syongari return (-1); 230136944Syongari if (in) 231136944Syongari *eax = pm1_control; 232136944Syongari else { 233136944Syongari /* 234246128Ssbz * Various bits are write-only or reserved, so force them 235246128Ssbz * to zero in pm1_control. Always preserve SCI_EN as OSPM 236136944Syongari * can never change it. 237136944Syongari */ 238136944Syongari pm1_control = (pm1_control & PM1_SCI_EN) | 239136944Syongari (*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO)); 240136944Syongari 241136944Syongari /* 242136944Syongari * If SLP_EN is set, check for S5. Bhyve's _S5_ method 243136944Syongari * says that '5' should be stored in SLP_TYP for S5. 244136944Syongari */ 245136944Syongari if (*eax & PM1_SLP_EN) { 246136944Syongari if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) 247136944Syongari return (INOUT_POWEROFF); 248136944Syongari } 249136944Syongari } 250136944Syongari return (0); 251136944Syongari} 252136944SyongariINOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler); 253246128SsbzSYSRES_IO(PM1A_EVT_ADDR, 8); 254246128Ssbz 255136944Syongari/* 256136944Syongari * ACPI SMI Command Register 257136944Syongari * 258136944Syongari * This write-only register is used to enable and disable ACPI. 259136944Syongari */ 260136944Syongaristatic int 261136944Syongarismi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 262136944Syongari uint32_t *eax, void *arg) 263136944Syongari{ 264136944Syongari 265136944Syongari assert(!in); 266136944Syongari if (bytes != 1) 267136944Syongari return (-1); 268136944Syongari 269193640Sariff pthread_mutex_lock(&pm_lock); 270193640Sariff switch (*eax) { 271193640Sariff case BHYVE_ACPI_ENABLE: 272193640Sariff pm1_control |= PM1_SCI_EN; 273193640Sariff if (power_button == NULL) { 274193640Sariff power_button = mevent_add(SIGTERM, EVF_SIGNAL, 275193640Sariff power_button_handler, ctx); 276193640Sariff old_power_handler = signal(SIGTERM, SIG_IGN); 277193640Sariff } 278193667Sariff break; 279193640Sariff case BHYVE_ACPI_DISABLE: 280193640Sariff pm1_control &= ~PM1_SCI_EN; 281136944Syongari if (power_button != NULL) { 282136944Syongari mevent_delete(power_button); 283136944Syongari power_button = NULL; 284136944Syongari signal(SIGTERM, old_power_handler); 285136944Syongari } 286136944Syongari break; 287136944Syongari } 288136944Syongari pthread_mutex_unlock(&pm_lock); 289136944Syongari return (0); 290136944Syongari} 291136944SyongariINOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler); 292136944SyongariSYSRES_IO(SMI_CMD, 1); 293136944Syongari 294136944Syongarivoid 295136944Syongarisci_init(struct vmctx *ctx) 296136944Syongari{ 297193640Sariff 298136944Syongari /* 299136944Syongari * Mark ACPI's SCI as level trigger and bump its use count 300136944Syongari * in the PIRQ router. 301136944Syongari */ 302136944Syongari pci_irq_use(SCI_INT); 303136944Syongari vm_isa_set_irq_trigger(ctx, SCI_INT, LEVEL_TRIGGER); 304136944Syongari} 305136944Syongari