pm.c revision 268972
168796Salfred/*-
268796Salfred * Copyright (c) 2013 Advanced Computing Technologies LLC
368796Salfred * Written by: John H. Baldwin <jhb@FreeBSD.org>
468796Salfred * All rights reserved.
568796Salfred *
668796Salfred * Redistribution and use in source and binary forms, with or without
768796Salfred * modification, are permitted provided that the following conditions
868796Salfred * are met:
968796Salfred * 1. Redistributions of source code must retain the above copyright
1068796Salfred *    notice, this list of conditions and the following disclaimer.
1168796Salfred * 2. Redistributions in binary form must reproduce the above copyright
1268796Salfred *    notice, this list of conditions and the following disclaimer in the
1368796Salfred *    documentation and/or other materials provided with the distribution.
1468796Salfred *
1568796Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1668796Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1768796Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1868796Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1968796Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2068796Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2168796Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2268796Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2368796Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2468796Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2568796Salfred * SUCH DAMAGE.
2668796Salfred */
2768796Salfred
2868796Salfred#include <sys/cdefs.h>
29206622Suqs__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/pm.c 268972 2014-07-22 03:14:37Z jhb $");
3079538Sru
3168796Salfred#include <sys/types.h>
3268796Salfred#include <machine/vmm.h>
3368796Salfred
3468796Salfred#include <assert.h>
3568796Salfred#include <pthread.h>
3668796Salfred#include <signal.h>
3768796Salfred#include <vmmapi.h>
3868796Salfred
3977279Solgeni#include "acpi.h"
4068796Salfred#include "inout.h"
4168853Sben#include "mevent.h"
4268796Salfred#include "pci_irq.h"
4368853Sben#include "pci_lpc.h"
4468796Salfred
4568796Salfredstatic pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
4668796Salfredstatic struct mevent *power_button;
4768796Salfredstatic sig_t old_power_handler;
4868796Salfred
4968796Salfred/*
5068796Salfred * Reset Control register at I/O port 0xcf9.  Bit 2 forces a system
5168796Salfred * reset when it transitions from 0 to 1.  Bit 1 selects the type of
52140458Sru * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
5368796Salfred * reset.
54108257Sru */
55108257Srustatic int
56108257Srureset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
5768796Salfred    uint32_t *eax, void *arg)
5868796Salfred{
5968796Salfred	static uint8_t reset_control;
6068796Salfred
6168796Salfred	if (bytes != 1)
6268796Salfred		return (-1);
6368796Salfred	if (in)
6468796Salfred		*eax = reset_control;
6568796Salfred	else {
6668796Salfred		reset_control = *eax;
6768796Salfred
6879366Sru		/* Treat hard and soft resets the same. */
6979366Sru		if (reset_control & 0x4)
70180595Sdwmalone			return (INOUT_RESET);
7168796Salfred	}
7268796Salfred	return (0);
7368796Salfred}
7468796SalfredINOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
7568796Salfred
7668796Salfred/*
7768796Salfred * ACPI's SCI is a level-triggered interrupt.
7877279Solgeni */
79static int sci_active;
80
81static void
82sci_assert(struct vmctx *ctx)
83{
84
85	if (sci_active)
86		return;
87	vm_isa_assert_irq(ctx, SCI_INT, SCI_INT);
88	sci_active = 1;
89}
90
91static void
92sci_deassert(struct vmctx *ctx)
93{
94
95	if (!sci_active)
96		return;
97	vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT);
98	sci_active = 0;
99}
100
101/*
102 * Power Management 1 Event Registers
103 *
104 * The only power management event supported is a power button upon
105 * receiving SIGTERM.
106 */
107static uint16_t pm1_enable, pm1_status;
108
109#define	PM1_TMR_STS		0x0001
110#define	PM1_BM_STS		0x0010
111#define	PM1_GBL_STS		0x0020
112#define	PM1_PWRBTN_STS		0x0100
113#define	PM1_SLPBTN_STS		0x0200
114#define	PM1_RTC_STS		0x0400
115#define	PM1_WAK_STS		0x8000
116
117#define	PM1_TMR_EN		0x0001
118#define	PM1_GBL_EN		0x0020
119#define	PM1_PWRBTN_EN		0x0100
120#define	PM1_SLPBTN_EN		0x0200
121#define	PM1_RTC_EN		0x0400
122
123static void
124sci_update(struct vmctx *ctx)
125{
126	int need_sci;
127
128	/* See if the SCI should be active or not. */
129	need_sci = 0;
130	if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS))
131		need_sci = 1;
132	if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS))
133		need_sci = 1;
134	if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS))
135		need_sci = 1;
136	if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS))
137		need_sci = 1;
138	if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS))
139		need_sci = 1;
140	if (need_sci)
141		sci_assert(ctx);
142	else
143		sci_deassert(ctx);
144}
145
146static int
147pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
148    uint32_t *eax, void *arg)
149{
150
151	if (bytes != 2)
152		return (-1);
153
154	pthread_mutex_lock(&pm_lock);
155	if (in)
156		*eax = pm1_status;
157	else {
158		/*
159		 * Writes are only permitted to clear certain bits by
160		 * writing 1 to those flags.
161		 */
162		pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS |
163		    PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS));
164		sci_update(ctx);
165	}
166	pthread_mutex_unlock(&pm_lock);
167	return (0);
168}
169
170static int
171pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
172    uint32_t *eax, void *arg)
173{
174
175	if (bytes != 2)
176		return (-1);
177
178	pthread_mutex_lock(&pm_lock);
179	if (in)
180		*eax = pm1_enable;
181	else {
182		/*
183		 * Only permit certain bits to be set.  We never use
184		 * the global lock, but ACPI-CA whines profusely if it
185		 * can't set GBL_EN.
186		 */
187		pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN);
188		sci_update(ctx);
189	}
190	pthread_mutex_unlock(&pm_lock);
191	return (0);
192}
193INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
194INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
195
196static void
197power_button_handler(int signal, enum ev_type type, void *arg)
198{
199	struct vmctx *ctx;
200
201	ctx = arg;
202	pthread_mutex_lock(&pm_lock);
203	if (!(pm1_status & PM1_PWRBTN_STS)) {
204		pm1_status |= PM1_PWRBTN_STS;
205		sci_update(ctx);
206	}
207	pthread_mutex_unlock(&pm_lock);
208}
209
210/*
211 * Power Management 1 Control Register
212 *
213 * This is mostly unimplemented except that we wish to handle writes that
214 * set SPL_EN to handle S5 (soft power off).
215 */
216static uint16_t pm1_control;
217
218#define	PM1_SCI_EN	0x0001
219#define	PM1_SLP_TYP	0x1c00
220#define	PM1_SLP_EN	0x2000
221#define	PM1_ALWAYS_ZERO	0xc003
222
223static int
224pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
225    uint32_t *eax, void *arg)
226{
227
228	if (bytes != 2)
229		return (-1);
230	if (in)
231		*eax = pm1_control;
232	else {
233		/*
234		 * Various bits are write-only or reserved, so force them
235		 * to zero in pm1_control.  Always preserve SCI_EN as OSPM
236		 * can never change it.
237		 */
238		pm1_control = (pm1_control & PM1_SCI_EN) |
239		    (*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO));
240
241		/*
242		 * If SLP_EN is set, check for S5.  Bhyve's _S5_ method
243		 * says that '5' should be stored in SLP_TYP for S5.
244		 */
245		if (*eax & PM1_SLP_EN) {
246			if ((pm1_control & PM1_SLP_TYP) >> 10 == 5)
247				return (INOUT_POWEROFF);
248		}
249	}
250	return (0);
251}
252INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
253SYSRES_IO(PM1A_EVT_ADDR, 8);
254
255/*
256 * ACPI SMI Command Register
257 *
258 * This write-only register is used to enable and disable ACPI.
259 */
260static int
261smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
262    uint32_t *eax, void *arg)
263{
264
265	assert(!in);
266	if (bytes != 1)
267		return (-1);
268
269	pthread_mutex_lock(&pm_lock);
270	switch (*eax) {
271	case BHYVE_ACPI_ENABLE:
272		pm1_control |= PM1_SCI_EN;
273		if (power_button == NULL) {
274			power_button = mevent_add(SIGTERM, EVF_SIGNAL,
275			    power_button_handler, ctx);
276			old_power_handler = signal(SIGTERM, SIG_IGN);
277		}
278		break;
279	case BHYVE_ACPI_DISABLE:
280		pm1_control &= ~PM1_SCI_EN;
281		if (power_button != NULL) {
282			mevent_delete(power_button);
283			power_button = NULL;
284			signal(SIGTERM, old_power_handler);
285		}
286		break;
287	}
288	pthread_mutex_unlock(&pm_lock);
289	return (0);
290}
291INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
292SYSRES_IO(SMI_CMD, 1);
293
294void
295sci_init(struct vmctx *ctx)
296{
297
298	/*
299	 * Mark ACPI's SCI as level trigger and bump its use count
300	 * in the PIRQ router.
301	 */
302	pci_irq_use(SCI_INT);
303	vm_isa_set_irq_trigger(ctx, SCI_INT, LEVEL_TRIGGER);
304}
305