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