xref: /freebsd/usr.sbin/bhyve/amd64/pm.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 Hudson River Trading LLC
5  * Written by: John H. Baldwin <jhb@FreeBSD.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 #include <sys/types.h>
32 #include <machine/vmm.h>
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <pthread.h>
37 #include <signal.h>
38 #include <vmmapi.h>
39 
40 #include "acpi.h"
41 #include "inout.h"
42 #include "mevent.h"
43 #include "pci_irq.h"
44 #include "pci_lpc.h"
45 
46 static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
47 static struct mevent *power_button;
48 static sig_t old_power_handler;
49 
50 static unsigned gpe0_active;
51 static unsigned gpe0_enabled;
52 static const unsigned gpe0_valid = (1u << GPE_VMGENC);
53 
54 /*
55  * Reset Control register at I/O port 0xcf9.  Bit 2 forces a system
56  * reset when it transitions from 0 to 1.  Bit 1 selects the type of
57  * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
58  * reset.
59  */
60 static int
61 reset_handler(struct vmctx *ctx __unused, int in,
62     int port __unused, int bytes, uint32_t *eax, void *arg __unused)
63 {
64 	int error;
65 
66 	static uint8_t reset_control;
67 
68 	if (bytes != 1)
69 		return (-1);
70 	if (in)
71 		*eax = reset_control;
72 	else {
73 		reset_control = *eax;
74 
75 		/* Treat hard and soft resets the same. */
76 		if (reset_control & 0x4) {
77 			error = vm_suspend(ctx, VM_SUSPEND_RESET);
78 			assert(error == 0 || errno == EALREADY);
79 		}
80 	}
81 	return (0);
82 }
83 INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
84 
85 /*
86  * ACPI's SCI is a level-triggered interrupt.
87  */
88 static int sci_active;
89 
90 static void
91 sci_assert(struct vmctx *ctx)
92 {
93 
94 	if (sci_active)
95 		return;
96 	vm_isa_assert_irq(ctx, SCI_INT, SCI_INT);
97 	sci_active = 1;
98 }
99 
100 static void
101 sci_deassert(struct vmctx *ctx)
102 {
103 
104 	if (!sci_active)
105 		return;
106 	vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT);
107 	sci_active = 0;
108 }
109 
110 /*
111  * Power Management 1 Event Registers
112  *
113  * The only power management event supported is a power button upon
114  * receiving SIGTERM.
115  */
116 static uint16_t pm1_enable, pm1_status;
117 
118 #define	PM1_TMR_STS		0x0001
119 #define	PM1_BM_STS		0x0010
120 #define	PM1_GBL_STS		0x0020
121 #define	PM1_PWRBTN_STS		0x0100
122 #define	PM1_SLPBTN_STS		0x0200
123 #define	PM1_RTC_STS		0x0400
124 #define	PM1_WAK_STS		0x8000
125 
126 #define	PM1_TMR_EN		0x0001
127 #define	PM1_GBL_EN		0x0020
128 #define	PM1_PWRBTN_EN		0x0100
129 #define	PM1_SLPBTN_EN		0x0200
130 #define	PM1_RTC_EN		0x0400
131 
132 static void
133 sci_update(struct vmctx *ctx)
134 {
135 	int need_sci;
136 
137 	/* See if the SCI should be active or not. */
138 	need_sci = 0;
139 	if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS))
140 		need_sci = 1;
141 	if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS))
142 		need_sci = 1;
143 	if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS))
144 		need_sci = 1;
145 	if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS))
146 		need_sci = 1;
147 	if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS))
148 		need_sci = 1;
149 	if ((gpe0_enabled & gpe0_active) != 0)
150 		need_sci = 1;
151 
152 	if (need_sci)
153 		sci_assert(ctx);
154 	else
155 		sci_deassert(ctx);
156 }
157 
158 static int
159 pm1_status_handler(struct vmctx *ctx, int in,
160     int port __unused, int bytes, uint32_t *eax, void *arg __unused)
161 {
162 
163 	if (bytes != 2)
164 		return (-1);
165 
166 	pthread_mutex_lock(&pm_lock);
167 	if (in)
168 		*eax = pm1_status;
169 	else {
170 		/*
171 		 * Writes are only permitted to clear certain bits by
172 		 * writing 1 to those flags.
173 		 */
174 		pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS |
175 		    PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS));
176 		sci_update(ctx);
177 	}
178 	pthread_mutex_unlock(&pm_lock);
179 	return (0);
180 }
181 
182 static int
183 pm1_enable_handler(struct vmctx *ctx, int in,
184     int port __unused, int bytes, uint32_t *eax, void *arg __unused)
185 {
186 
187 	if (bytes != 2)
188 		return (-1);
189 
190 	pthread_mutex_lock(&pm_lock);
191 	if (in)
192 		*eax = pm1_enable;
193 	else {
194 		/*
195 		 * Only permit certain bits to be set.  We never use
196 		 * the global lock, but ACPI-CA whines profusely if it
197 		 * can't set GBL_EN.
198 		 */
199 		pm1_enable = *eax & (PM1_RTC_EN | PM1_PWRBTN_EN | PM1_GBL_EN);
200 		sci_update(ctx);
201 	}
202 	pthread_mutex_unlock(&pm_lock);
203 	return (0);
204 }
205 INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
206 INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
207 
208 static void
209 power_button_handler(int signal __unused, enum ev_type type __unused, void *arg)
210 {
211 	struct vmctx *ctx;
212 
213 	ctx = arg;
214 	pthread_mutex_lock(&pm_lock);
215 	if (!(pm1_status & PM1_PWRBTN_STS)) {
216 		pm1_status |= PM1_PWRBTN_STS;
217 		sci_update(ctx);
218 	}
219 	pthread_mutex_unlock(&pm_lock);
220 }
221 
222 /*
223  * Power Management 1 Control Register
224  *
225  * This is mostly unimplemented except that we wish to handle writes that
226  * set SPL_EN to handle S5 (soft power off).
227  */
228 static uint16_t pm1_control;
229 
230 #define	PM1_SCI_EN	0x0001
231 #define	PM1_SLP_TYP	0x1c00
232 #define	PM1_SLP_EN	0x2000
233 #define	PM1_ALWAYS_ZERO	0xc003
234 
235 static int
236 pm1_control_handler(struct vmctx *ctx, int in,
237     int port __unused, int bytes, uint32_t *eax, void *arg __unused)
238 {
239 	int error;
240 
241 	if (bytes != 2)
242 		return (-1);
243 	if (in)
244 		*eax = pm1_control;
245 	else {
246 		/*
247 		 * Various bits are write-only or reserved, so force them
248 		 * to zero in pm1_control.  Always preserve SCI_EN as OSPM
249 		 * can never change it.
250 		 */
251 		pm1_control = (pm1_control & PM1_SCI_EN) |
252 		    (*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO));
253 
254 		/*
255 		 * If SLP_EN is set, check for S5.  Bhyve's _S5_ method
256 		 * says that '5' should be stored in SLP_TYP for S5.
257 		 */
258 		if (*eax & PM1_SLP_EN) {
259 			if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) {
260 				error = vm_suspend(ctx, VM_SUSPEND_POWEROFF);
261 				assert(error == 0 || errno == EALREADY);
262 			}
263 		}
264 	}
265 	return (0);
266 }
267 INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
268 SYSRES_IO(PM1A_EVT_ADDR, 8);
269 
270 void
271 acpi_raise_gpe(struct vmctx *ctx, unsigned bit)
272 {
273 	unsigned mask;
274 
275 	assert(bit < (IO_GPE0_LEN * (8 / 2)));
276 	mask = (1u << bit);
277 	assert((mask & ~gpe0_valid) == 0);
278 
279 	pthread_mutex_lock(&pm_lock);
280 	gpe0_active |= mask;
281 	sci_update(ctx);
282 	pthread_mutex_unlock(&pm_lock);
283 }
284 
285 static int
286 gpe0_sts(struct vmctx *ctx, int in, int port __unused,
287     int bytes, uint32_t *eax, void *arg __unused)
288 {
289 	/*
290 	 * ACPI 6.2 specifies the GPE register blocks are accessed
291 	 * byte-at-a-time.
292 	 */
293 	if (bytes != 1)
294 		return (-1);
295 
296 	pthread_mutex_lock(&pm_lock);
297 	if (in)
298 		*eax = gpe0_active;
299 	else {
300 		/* W1C */
301 		gpe0_active &= ~(*eax & gpe0_valid);
302 		sci_update(ctx);
303 	}
304 	pthread_mutex_unlock(&pm_lock);
305 	return (0);
306 }
307 INOUT_PORT(gpe0_sts, IO_GPE0_STS, IOPORT_F_INOUT, gpe0_sts);
308 
309 static int
310 gpe0_en(struct vmctx *ctx, int in, int port __unused,
311     int bytes, uint32_t *eax, void *arg __unused)
312 {
313 	if (bytes != 1)
314 		return (-1);
315 
316 	pthread_mutex_lock(&pm_lock);
317 	if (in)
318 		*eax = gpe0_enabled;
319 	else {
320 		gpe0_enabled = (*eax & gpe0_valid);
321 		sci_update(ctx);
322 	}
323 	pthread_mutex_unlock(&pm_lock);
324 	return (0);
325 }
326 INOUT_PORT(gpe0_en, IO_GPE0_EN, IOPORT_F_INOUT, gpe0_en);
327 
328 /*
329  * ACPI SMI Command Register
330  *
331  * This write-only register is used to enable and disable ACPI.
332  */
333 static int
334 smi_cmd_handler(struct vmctx *ctx, int in, int port __unused,
335     int bytes, uint32_t *eax, void *arg __unused)
336 {
337 
338 	assert(!in);
339 	if (bytes != 1)
340 		return (-1);
341 
342 	pthread_mutex_lock(&pm_lock);
343 	switch (*eax) {
344 	case BHYVE_ACPI_ENABLE:
345 		pm1_control |= PM1_SCI_EN;
346 		if (power_button == NULL) {
347 			power_button = mevent_add(SIGTERM, EVF_SIGNAL,
348 			    power_button_handler, ctx);
349 			old_power_handler = signal(SIGTERM, SIG_IGN);
350 		}
351 		break;
352 	case BHYVE_ACPI_DISABLE:
353 		pm1_control &= ~PM1_SCI_EN;
354 		if (power_button != NULL) {
355 			mevent_delete(power_button);
356 			power_button = NULL;
357 			signal(SIGTERM, old_power_handler);
358 		}
359 		break;
360 	}
361 	pthread_mutex_unlock(&pm_lock);
362 	return (0);
363 }
364 INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
365 SYSRES_IO(SMI_CMD, 1);
366 
367 void
368 sci_init(struct vmctx *ctx)
369 {
370 
371 	/*
372 	 * Mark ACPI's SCI as level trigger and bump its use count
373 	 * in the PIRQ router.
374 	 */
375 	pci_irq_use(SCI_INT);
376 	vm_isa_set_irq_trigger(ctx, SCI_INT, LEVEL_TRIGGER);
377 }
378