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