1259826Sjhb/*- 2259826Sjhb * Copyright (c) 2013 Advanced Computing Technologies LLC 3259826Sjhb * Written by: John H. Baldwin <jhb@FreeBSD.org> 4259826Sjhb * All rights reserved. 5259826Sjhb * 6259826Sjhb * Redistribution and use in source and binary forms, with or without 7259826Sjhb * modification, are permitted provided that the following conditions 8259826Sjhb * are met: 9259826Sjhb * 1. Redistributions of source code must retain the above copyright 10259826Sjhb * notice, this list of conditions and the following disclaimer. 11259826Sjhb * 2. Redistributions in binary form must reproduce the above copyright 12259826Sjhb * notice, this list of conditions and the following disclaimer in the 13259826Sjhb * documentation and/or other materials provided with the distribution. 14259826Sjhb * 15259826Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16259826Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17259826Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18259826Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19259826Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20259826Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21259826Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22259826Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23259826Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24259826Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25259826Sjhb * SUCH DAMAGE. 26259826Sjhb */ 27259826Sjhb 28259826Sjhb#include <sys/cdefs.h> 29259826Sjhb__FBSDID("$FreeBSD$"); 30259826Sjhb 31259826Sjhb#include <sys/types.h> 32261090Sjhb#include <machine/vmm.h> 33259826Sjhb 34261090Sjhb#include <assert.h> 35270159Sgrehan#include <errno.h> 36261090Sjhb#include <pthread.h> 37261090Sjhb#include <signal.h> 38261090Sjhb#include <vmmapi.h> 39261090Sjhb 40261090Sjhb#include "acpi.h" 41259826Sjhb#include "inout.h" 42261090Sjhb#include "mevent.h" 43268972Sjhb#include "pci_irq.h" 44261265Sjhb#include "pci_lpc.h" 45259826Sjhb 46261090Sjhbstatic pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER; 47261090Sjhbstatic struct mevent *power_button; 48261090Sjhbstatic sig_t old_power_handler; 49259826Sjhb 50259826Sjhb/* 51259826Sjhb * Reset Control register at I/O port 0xcf9. Bit 2 forces a system 52259826Sjhb * reset when it transitions from 0 to 1. Bit 1 selects the type of 53259826Sjhb * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard" 54259826Sjhb * reset. 55259826Sjhb */ 56259826Sjhbstatic int 57259826Sjhbreset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 58259826Sjhb uint32_t *eax, void *arg) 59259826Sjhb{ 60270159Sgrehan int error; 61270159Sgrehan 62259826Sjhb static uint8_t reset_control; 63259826Sjhb 64259826Sjhb if (bytes != 1) 65259826Sjhb return (-1); 66259826Sjhb if (in) 67259826Sjhb *eax = reset_control; 68259826Sjhb else { 69259826Sjhb reset_control = *eax; 70259826Sjhb 71259826Sjhb /* Treat hard and soft resets the same. */ 72270159Sgrehan if (reset_control & 0x4) { 73270159Sgrehan error = vm_suspend(ctx, VM_SUSPEND_RESET); 74270159Sgrehan assert(error == 0 || errno == EALREADY); 75270159Sgrehan } 76259826Sjhb } 77259826Sjhb return (0); 78259826Sjhb} 79259826SjhbINOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler); 80259826Sjhb 81259826Sjhb/* 82261090Sjhb * ACPI's SCI is a level-triggered interrupt. 83261090Sjhb */ 84261090Sjhbstatic int sci_active; 85261090Sjhb 86261090Sjhbstatic void 87261090Sjhbsci_assert(struct vmctx *ctx) 88261090Sjhb{ 89261090Sjhb 90261090Sjhb if (sci_active) 91261090Sjhb return; 92268891Sjhb vm_isa_assert_irq(ctx, SCI_INT, SCI_INT); 93261090Sjhb sci_active = 1; 94261090Sjhb} 95261090Sjhb 96261090Sjhbstatic void 97261090Sjhbsci_deassert(struct vmctx *ctx) 98261090Sjhb{ 99261090Sjhb 100261090Sjhb if (!sci_active) 101261090Sjhb return; 102268891Sjhb vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT); 103261090Sjhb sci_active = 0; 104261090Sjhb} 105261090Sjhb 106261090Sjhb/* 107259826Sjhb * Power Management 1 Event Registers 108259826Sjhb * 109261090Sjhb * The only power management event supported is a power button upon 110261090Sjhb * receiving SIGTERM. 111259826Sjhb */ 112261090Sjhbstatic uint16_t pm1_enable, pm1_status; 113261090Sjhb 114261090Sjhb#define PM1_TMR_STS 0x0001 115261090Sjhb#define PM1_BM_STS 0x0010 116261090Sjhb#define PM1_GBL_STS 0x0020 117261090Sjhb#define PM1_PWRBTN_STS 0x0100 118261090Sjhb#define PM1_SLPBTN_STS 0x0200 119261090Sjhb#define PM1_RTC_STS 0x0400 120261090Sjhb#define PM1_WAK_STS 0x8000 121261090Sjhb 122261090Sjhb#define PM1_TMR_EN 0x0001 123261090Sjhb#define PM1_GBL_EN 0x0020 124261090Sjhb#define PM1_PWRBTN_EN 0x0100 125261090Sjhb#define PM1_SLPBTN_EN 0x0200 126261090Sjhb#define PM1_RTC_EN 0x0400 127261090Sjhb 128261090Sjhbstatic void 129261090Sjhbsci_update(struct vmctx *ctx) 130261090Sjhb{ 131261090Sjhb int need_sci; 132261090Sjhb 133261090Sjhb /* See if the SCI should be active or not. */ 134261090Sjhb need_sci = 0; 135261090Sjhb if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS)) 136261090Sjhb need_sci = 1; 137261090Sjhb if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS)) 138261090Sjhb need_sci = 1; 139261090Sjhb if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS)) 140261090Sjhb need_sci = 1; 141261090Sjhb if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS)) 142261090Sjhb need_sci = 1; 143261090Sjhb if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS)) 144261090Sjhb need_sci = 1; 145261090Sjhb if (need_sci) 146261090Sjhb sci_assert(ctx); 147261090Sjhb else 148261090Sjhb sci_deassert(ctx); 149261090Sjhb} 150261090Sjhb 151259826Sjhbstatic int 152259826Sjhbpm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 153259826Sjhb uint32_t *eax, void *arg) 154259826Sjhb{ 155259826Sjhb 156259826Sjhb if (bytes != 2) 157259826Sjhb return (-1); 158261090Sjhb 159261090Sjhb pthread_mutex_lock(&pm_lock); 160259826Sjhb if (in) 161261090Sjhb *eax = pm1_status; 162261090Sjhb else { 163261090Sjhb /* 164261090Sjhb * Writes are only permitted to clear certain bits by 165261090Sjhb * writing 1 to those flags. 166261090Sjhb */ 167261090Sjhb pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS | 168261090Sjhb PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS)); 169261090Sjhb sci_update(ctx); 170261090Sjhb } 171261090Sjhb pthread_mutex_unlock(&pm_lock); 172259826Sjhb return (0); 173259826Sjhb} 174259826Sjhb 175259826Sjhbstatic int 176259826Sjhbpm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 177259826Sjhb uint32_t *eax, void *arg) 178259826Sjhb{ 179259826Sjhb 180259826Sjhb if (bytes != 2) 181259826Sjhb return (-1); 182261090Sjhb 183261090Sjhb pthread_mutex_lock(&pm_lock); 184259826Sjhb if (in) 185259826Sjhb *eax = pm1_enable; 186261090Sjhb else { 187261090Sjhb /* 188261090Sjhb * Only permit certain bits to be set. We never use 189261090Sjhb * the global lock, but ACPI-CA whines profusely if it 190261090Sjhb * can't set GBL_EN. 191261090Sjhb */ 192261090Sjhb pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN); 193261090Sjhb sci_update(ctx); 194261090Sjhb } 195261090Sjhb pthread_mutex_unlock(&pm_lock); 196259826Sjhb return (0); 197259826Sjhb} 198259826SjhbINOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler); 199259826SjhbINOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler); 200259826Sjhb 201261090Sjhbstatic void 202261090Sjhbpower_button_handler(int signal, enum ev_type type, void *arg) 203261090Sjhb{ 204261090Sjhb struct vmctx *ctx; 205261090Sjhb 206261090Sjhb ctx = arg; 207261090Sjhb pthread_mutex_lock(&pm_lock); 208261090Sjhb if (!(pm1_status & PM1_PWRBTN_STS)) { 209261090Sjhb pm1_status |= PM1_PWRBTN_STS; 210261090Sjhb sci_update(ctx); 211261090Sjhb } 212261090Sjhb pthread_mutex_unlock(&pm_lock); 213261090Sjhb} 214261090Sjhb 215259826Sjhb/* 216259826Sjhb * Power Management 1 Control Register 217259826Sjhb * 218259826Sjhb * This is mostly unimplemented except that we wish to handle writes that 219259826Sjhb * set SPL_EN to handle S5 (soft power off). 220259826Sjhb */ 221261090Sjhbstatic uint16_t pm1_control; 222261090Sjhb 223261090Sjhb#define PM1_SCI_EN 0x0001 224259826Sjhb#define PM1_SLP_TYP 0x1c00 225259826Sjhb#define PM1_SLP_EN 0x2000 226259826Sjhb#define PM1_ALWAYS_ZERO 0xc003 227259826Sjhb 228259826Sjhbstatic int 229259826Sjhbpm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 230259826Sjhb uint32_t *eax, void *arg) 231259826Sjhb{ 232270159Sgrehan int error; 233259826Sjhb 234259826Sjhb if (bytes != 2) 235259826Sjhb return (-1); 236259826Sjhb if (in) 237259826Sjhb *eax = pm1_control; 238259826Sjhb else { 239259826Sjhb /* 240259826Sjhb * Various bits are write-only or reserved, so force them 241261090Sjhb * to zero in pm1_control. Always preserve SCI_EN as OSPM 242261090Sjhb * can never change it. 243259826Sjhb */ 244261090Sjhb pm1_control = (pm1_control & PM1_SCI_EN) | 245261090Sjhb (*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO)); 246259826Sjhb 247259826Sjhb /* 248259826Sjhb * If SLP_EN is set, check for S5. Bhyve's _S5_ method 249259826Sjhb * says that '5' should be stored in SLP_TYP for S5. 250259826Sjhb */ 251259826Sjhb if (*eax & PM1_SLP_EN) { 252270159Sgrehan if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) { 253270159Sgrehan error = vm_suspend(ctx, VM_SUSPEND_POWEROFF); 254270159Sgrehan assert(error == 0 || errno == EALREADY); 255270159Sgrehan } 256259826Sjhb } 257259826Sjhb } 258259826Sjhb return (0); 259259826Sjhb} 260259826SjhbINOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler); 261261265SjhbSYSRES_IO(PM1A_EVT_ADDR, 8); 262261090Sjhb 263261090Sjhb/* 264261090Sjhb * ACPI SMI Command Register 265261090Sjhb * 266261090Sjhb * This write-only register is used to enable and disable ACPI. 267261090Sjhb */ 268261090Sjhbstatic int 269261090Sjhbsmi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 270261090Sjhb uint32_t *eax, void *arg) 271261090Sjhb{ 272261090Sjhb 273261090Sjhb assert(!in); 274261090Sjhb if (bytes != 1) 275261090Sjhb return (-1); 276261090Sjhb 277261090Sjhb pthread_mutex_lock(&pm_lock); 278261090Sjhb switch (*eax) { 279261090Sjhb case BHYVE_ACPI_ENABLE: 280261090Sjhb pm1_control |= PM1_SCI_EN; 281261090Sjhb if (power_button == NULL) { 282261090Sjhb power_button = mevent_add(SIGTERM, EVF_SIGNAL, 283261090Sjhb power_button_handler, ctx); 284261090Sjhb old_power_handler = signal(SIGTERM, SIG_IGN); 285261090Sjhb } 286261090Sjhb break; 287261090Sjhb case BHYVE_ACPI_DISABLE: 288261090Sjhb pm1_control &= ~PM1_SCI_EN; 289261090Sjhb if (power_button != NULL) { 290261090Sjhb mevent_delete(power_button); 291261090Sjhb power_button = NULL; 292261090Sjhb signal(SIGTERM, old_power_handler); 293261090Sjhb } 294261090Sjhb break; 295261090Sjhb } 296261090Sjhb pthread_mutex_unlock(&pm_lock); 297261090Sjhb return (0); 298261090Sjhb} 299261090SjhbINOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler); 300261265SjhbSYSRES_IO(SMI_CMD, 1); 301268972Sjhb 302268972Sjhbvoid 303268972Sjhbsci_init(struct vmctx *ctx) 304268972Sjhb{ 305268972Sjhb 306268972Sjhb /* 307268972Sjhb * Mark ACPI's SCI as level trigger and bump its use count 308268972Sjhb * in the PIRQ router. 309268972Sjhb */ 310268972Sjhb pci_irq_use(SCI_INT); 311268972Sjhb vm_isa_set_irq_trigger(ctx, SCI_INT, LEVEL_TRIGGER); 312268972Sjhb} 313