pm.c revision 261265
117683Spst/*- 217683Spst * Copyright (c) 2013 Advanced Computing Technologies LLC 317683Spst * Written by: John H. Baldwin <jhb@FreeBSD.org> 417683Spst * All rights reserved. 517683Spst * 617683Spst * Redistribution and use in source and binary forms, with or without 717683Spst * modification, are permitted provided that the following conditions 817683Spst * are met: 917683Spst * 1. Redistributions of source code must retain the above copyright 1017683Spst * notice, this list of conditions and the following disclaimer. 1117683Spst * 2. Redistributions in binary form must reproduce the above copyright 1217683Spst * notice, this list of conditions and the following disclaimer in the 1317683Spst * documentation and/or other materials provided with the distribution. 1417683Spst * 1517683Spst * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1617683Spst * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1717683Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1817683Spst * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1917683Spst * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2017683Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2117683Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2217683Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2317683Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24127664Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25214518Srpaulo * SUCH DAMAGE. 2617683Spst */ 2717683Spst 2875107Sfenner#include <sys/cdefs.h> 2975107Sfenner__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/pm.c 261265 2014-01-29 13:35:12Z jhb $"); 3075107Sfenner 3175107Sfenner#include <sys/types.h> 32214518Srpaulo#include <machine/vmm.h> 33214518Srpaulo 34214518Srpaulo#include <assert.h> 35214518Srpaulo#include <pthread.h> 36214518Srpaulo#include <signal.h> 37214518Srpaulo#include <vmmapi.h> 38214518Srpaulo 39214518Srpaulo#include "acpi.h" 40214518Srpaulo#include "inout.h" 41214518Srpaulo#include "mevent.h" 42214518Srpaulo#include "pci_lpc.h" 43214518Srpaulo 44214518Srpaulostatic pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER; 45214518Srpaulostatic struct mevent *power_button; 4617683Spststatic sig_t old_power_handler; 4717683Spst 4817683Spst/* 49146768Ssam * Reset Control register at I/O port 0xcf9. Bit 2 forces a system 5017683Spst * reset when it transitions from 0 to 1. Bit 1 selects the type of 5175107Sfenner * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard" 5275107Sfenner * reset. 5317683Spst */ 5417683Spststatic int 5517683Spstreset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 5617683Spst uint32_t *eax, void *arg) 5717683Spst{ 5817683Spst static uint8_t reset_control; 5917683Spst 6017683Spst if (bytes != 1) 6117683Spst return (-1); 6217683Spst if (in) 6317683Spst *eax = reset_control; 6417683Spst else { 65146768Ssam reset_control = *eax; 66146768Ssam 67146768Ssam /* Treat hard and soft resets the same. */ 68146768Ssam if (reset_control & 0x4) 6917683Spst return (INOUT_RESET); 70190225Srpaulo } 71190225Srpaulo return (0); 72190225Srpaulo} 73190225SrpauloINOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler); 74146768Ssam 75146768Ssam/* 76146768Ssam * ACPI's SCI is a level-triggered interrupt. 7717683Spst */ 7817683Spststatic int sci_active; 7917683Spst 80146768Ssamstatic void 81146768Ssamsci_assert(struct vmctx *ctx) 82146768Ssam{ 83146768Ssam 84146768Ssam if (sci_active) 85146768Ssam return; 86146768Ssam vm_ioapic_assert_irq(ctx, SCI_INT); 87146768Ssam sci_active = 1; 88146768Ssam} 8917683Spst 9017683Spststatic void 9117683Spstsci_deassert(struct vmctx *ctx) 9217683Spst{ 9317683Spst 9417683Spst if (!sci_active) 9517683Spst return; 9617683Spst vm_ioapic_deassert_irq(ctx, SCI_INT); 9717683Spst sci_active = 0; 9817683Spst} 9917683Spst 10017683Spst/* 10117683Spst * Power Management 1 Event Registers 10217683Spst * 10317683Spst * The only power management event supported is a power button upon 10417683Spst * receiving SIGTERM. 10517683Spst */ 10617683Spststatic uint16_t pm1_enable, pm1_status; 10717683Spst 10817683Spst#define PM1_TMR_STS 0x0001 10917683Spst#define PM1_BM_STS 0x0010 11017683Spst#define PM1_GBL_STS 0x0020 11117683Spst#define PM1_PWRBTN_STS 0x0100 11217683Spst#define PM1_SLPBTN_STS 0x0200 11317683Spst#define PM1_RTC_STS 0x0400 11417683Spst#define PM1_WAK_STS 0x8000 11517683Spst 11617683Spst#define PM1_TMR_EN 0x0001 11717683Spst#define PM1_GBL_EN 0x0020 11817683Spst#define PM1_PWRBTN_EN 0x0100 11917683Spst#define PM1_SLPBTN_EN 0x0200 12017683Spst#define PM1_RTC_EN 0x0400 12117683Spst 12217683Spststatic void 12317683Spstsci_update(struct vmctx *ctx) 12417683Spst{ 12517683Spst int need_sci; 12617683Spst 12717683Spst /* See if the SCI should be active or not. */ 12817683Spst need_sci = 0; 12917683Spst if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS)) 13017683Spst need_sci = 1; 13117683Spst if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS)) 13217683Spst need_sci = 1; 13317683Spst if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS)) 13417683Spst need_sci = 1; 13517683Spst if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS)) 13617683Spst need_sci = 1; 13717683Spst if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS)) 13817683Spst need_sci = 1; 13917683Spst if (need_sci) 14017683Spst sci_assert(ctx); 14117683Spst else 14217683Spst sci_deassert(ctx); 14317683Spst} 14417683Spst 14517683Spststatic int 14617683Spstpm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 14717683Spst uint32_t *eax, void *arg) 14817683Spst{ 14917683Spst 15017683Spst if (bytes != 2) 15117683Spst return (-1); 15217683Spst 15317683Spst pthread_mutex_lock(&pm_lock); 15417683Spst if (in) 15517683Spst *eax = pm1_status; 15617683Spst else { 15717683Spst /* 15817683Spst * Writes are only permitted to clear certain bits by 15917683Spst * writing 1 to those flags. 16017683Spst */ 16117683Spst pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS | 16217683Spst PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS)); 16317683Spst sci_update(ctx); 16417683Spst } 16517683Spst pthread_mutex_unlock(&pm_lock); 16617683Spst return (0); 16717683Spst} 16817683Spst 16917683Spststatic int 17017683Spstpm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 17117683Spst uint32_t *eax, void *arg) 17217683Spst{ 17317683Spst 17417683Spst if (bytes != 2) 17517683Spst return (-1); 17617683Spst 17717683Spst pthread_mutex_lock(&pm_lock); 17817683Spst if (in) 17917683Spst *eax = pm1_enable; 18017683Spst else { 18117683Spst /* 18217683Spst * Only permit certain bits to be set. We never use 18317683Spst * the global lock, but ACPI-CA whines profusely if it 18417683Spst * can't set GBL_EN. 18517683Spst */ 18617683Spst pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN); 18717683Spst sci_update(ctx); 18817683Spst } 18917683Spst pthread_mutex_unlock(&pm_lock); 19017683Spst return (0); 19117683Spst} 19217683SpstINOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler); 193251129SdelphijINOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler); 19417683Spst 19517683Spststatic void 19617683Spstpower_button_handler(int signal, enum ev_type type, void *arg) 19717683Spst{ 19817683Spst struct vmctx *ctx; 19917683Spst 20017683Spst ctx = arg; 20117683Spst pthread_mutex_lock(&pm_lock); 20217683Spst if (!(pm1_status & PM1_PWRBTN_STS)) { 20317683Spst pm1_status |= PM1_PWRBTN_STS; 20417683Spst sci_update(ctx); 20517683Spst } 20617683Spst pthread_mutex_unlock(&pm_lock); 20717683Spst} 20817683Spst 20917683Spst/* 21017683Spst * Power Management 1 Control Register 21117683Spst * 21217683Spst * This is mostly unimplemented except that we wish to handle writes that 21317683Spst * set SPL_EN to handle S5 (soft power off). 21417683Spst */ 21517683Spststatic uint16_t pm1_control; 21617683Spst 21717683Spst#define PM1_SCI_EN 0x0001 21817683Spst#define PM1_SLP_TYP 0x1c00 21917683Spst#define PM1_SLP_EN 0x2000 22017683Spst#define PM1_ALWAYS_ZERO 0xc003 221251129Sdelphij 22217683Spststatic int 22317683Spstpm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 22417683Spst uint32_t *eax, void *arg) 22517683Spst{ 22617683Spst 22717683Spst if (bytes != 2) 22817683Spst return (-1); 22917683Spst if (in) 23017683Spst *eax = pm1_control; 23117683Spst else { 23217683Spst /* 233251129Sdelphij * Various bits are write-only or reserved, so force them 23417683Spst * to zero in pm1_control. Always preserve SCI_EN as OSPM 23517683Spst * can never change it. 23617683Spst */ 23717683Spst pm1_control = (pm1_control & PM1_SCI_EN) | 23817683Spst (*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO)); 23917683Spst 24017683Spst /* 24117683Spst * If SLP_EN is set, check for S5. Bhyve's _S5_ method 24217683Spst * says that '5' should be stored in SLP_TYP for S5. 24317683Spst */ 24417683Spst if (*eax & PM1_SLP_EN) { 24517683Spst if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) 24617683Spst return (INOUT_POWEROFF); 24717683Spst } 24817683Spst } 24917683Spst return (0); 25017683Spst} 25117683SpstINOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler); 25217683SpstSYSRES_IO(PM1A_EVT_ADDR, 8); 25317683Spst 25417683Spst/* 25517683Spst * ACPI SMI Command Register 25617683Spst * 25717683Spst * This write-only register is used to enable and disable ACPI. 25817683Spst */ 25917683Spststatic int 26017683Spstsmi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 26117683Spst uint32_t *eax, void *arg) 26217683Spst{ 263251129Sdelphij 26417683Spst assert(!in); 26517683Spst if (bytes != 1) 26617683Spst return (-1); 26717683Spst 26817683Spst pthread_mutex_lock(&pm_lock); 26917683Spst switch (*eax) { 27017683Spst case BHYVE_ACPI_ENABLE: 27117683Spst pm1_control |= PM1_SCI_EN; 27217683Spst if (power_button == NULL) { 27317683Spst power_button = mevent_add(SIGTERM, EVF_SIGNAL, 27417683Spst power_button_handler, ctx); 27517683Spst old_power_handler = signal(SIGTERM, SIG_IGN); 27617683Spst } 277251129Sdelphij break; 27817683Spst case BHYVE_ACPI_DISABLE: 27917683Spst pm1_control &= ~PM1_SCI_EN; 28017683Spst if (power_button != NULL) { 28117683Spst mevent_delete(power_button); 28217683Spst power_button = NULL; 28317683Spst signal(SIGTERM, old_power_handler); 28417683Spst } 28517683Spst break; 28617683Spst } 28717683Spst pthread_mutex_unlock(&pm_lock); 28817683Spst return (0); 28917683Spst} 29017683SpstINOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler); 29117683SpstSYSRES_IO(SMI_CMD, 1); 29217683Spst