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