xref: /illumos-gate/usr/src/cmd/bhyve/pm.c (revision 8fff7887)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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  * Copyright 2018 Joyent, Inc.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/types.h>
37 #include <machine/vmm.h>
38 
39 #include <assert.h>
40 #include <errno.h>
41 #include <pthread.h>
42 #ifndef	__FreeBSD__
43 #include <stdlib.h>
44 #endif
45 #include <signal.h>
46 #include <vmmapi.h>
47 
48 #include "acpi.h"
49 #include "inout.h"
50 #ifdef	__FreeBSD__
51 #include "mevent.h"
52 #endif
53 #include "pci_irq.h"
54 #include "pci_lpc.h"
55 
56 static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
57 #ifdef	__FreeBSD__
58 static struct mevent *power_button;
59 static sig_t old_power_handler;
60 #else
61 struct vmctx *pwr_ctx;
62 #endif
63 
64 /*
65  * Reset Control register at I/O port 0xcf9.  Bit 2 forces a system
66  * reset when it transitions from 0 to 1.  Bit 1 selects the type of
67  * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
68  * reset.
69  */
70 static int
71 reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
72     uint32_t *eax, void *arg)
73 {
74 	int error;
75 
76 	static uint8_t reset_control;
77 
78 	if (bytes != 1)
79 		return (-1);
80 	if (in)
81 		*eax = reset_control;
82 	else {
83 		reset_control = *eax;
84 
85 		/* Treat hard and soft resets the same. */
86 		if (reset_control & 0x4) {
87 			error = vm_suspend(ctx, VM_SUSPEND_RESET);
88 			assert(error == 0 || errno == EALREADY);
89 		}
90 	}
91 	return (0);
92 }
93 INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
94 
95 /*
96  * ACPI's SCI is a level-triggered interrupt.
97  */
98 static int sci_active;
99 
100 static void
101 sci_assert(struct vmctx *ctx)
102 {
103 
104 	if (sci_active)
105 		return;
106 	vm_isa_assert_irq(ctx, SCI_INT, SCI_INT);
107 	sci_active = 1;
108 }
109 
110 static void
111 sci_deassert(struct vmctx *ctx)
112 {
113 
114 	if (!sci_active)
115 		return;
116 	vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT);
117 	sci_active = 0;
118 }
119 
120 /*
121  * Power Management 1 Event Registers
122  *
123  * The only power management event supported is a power button upon
124  * receiving SIGTERM.
125  */
126 static uint16_t pm1_enable, pm1_status;
127 
128 #define	PM1_TMR_STS		0x0001
129 #define	PM1_BM_STS		0x0010
130 #define	PM1_GBL_STS		0x0020
131 #define	PM1_PWRBTN_STS		0x0100
132 #define	PM1_SLPBTN_STS		0x0200
133 #define	PM1_RTC_STS		0x0400
134 #define	PM1_WAK_STS		0x8000
135 
136 #define	PM1_TMR_EN		0x0001
137 #define	PM1_GBL_EN		0x0020
138 #define	PM1_PWRBTN_EN		0x0100
139 #define	PM1_SLPBTN_EN		0x0200
140 #define	PM1_RTC_EN		0x0400
141 
142 static void
143 sci_update(struct vmctx *ctx)
144 {
145 	int need_sci;
146 
147 	/* See if the SCI should be active or not. */
148 	need_sci = 0;
149 	if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS))
150 		need_sci = 1;
151 	if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS))
152 		need_sci = 1;
153 	if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS))
154 		need_sci = 1;
155 	if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS))
156 		need_sci = 1;
157 	if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS))
158 		need_sci = 1;
159 	if (need_sci)
160 		sci_assert(ctx);
161 	else
162 		sci_deassert(ctx);
163 }
164 
165 static int
166 pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
167     uint32_t *eax, void *arg)
168 {
169 
170 	if (bytes != 2)
171 		return (-1);
172 
173 	pthread_mutex_lock(&pm_lock);
174 	if (in)
175 		*eax = pm1_status;
176 	else {
177 		/*
178 		 * Writes are only permitted to clear certain bits by
179 		 * writing 1 to those flags.
180 		 */
181 		pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS |
182 		    PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS));
183 		sci_update(ctx);
184 	}
185 	pthread_mutex_unlock(&pm_lock);
186 	return (0);
187 }
188 
189 static int
190 pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
191     uint32_t *eax, void *arg)
192 {
193 
194 	if (bytes != 2)
195 		return (-1);
196 
197 	pthread_mutex_lock(&pm_lock);
198 	if (in)
199 		*eax = pm1_enable;
200 	else {
201 		/*
202 		 * Only permit certain bits to be set.  We never use
203 		 * the global lock, but ACPI-CA whines profusely if it
204 		 * can't set GBL_EN.
205 		 */
206 		pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN);
207 		sci_update(ctx);
208 	}
209 	pthread_mutex_unlock(&pm_lock);
210 	return (0);
211 }
212 INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
213 INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
214 
215 #ifdef	__FreeBSD__
216 static void
217 power_button_handler(int signal, enum ev_type type, void *arg)
218 {
219 	struct vmctx *ctx;
220 
221 	ctx = arg;
222 	pthread_mutex_lock(&pm_lock);
223 	if (!(pm1_status & PM1_PWRBTN_STS)) {
224 		pm1_status |= PM1_PWRBTN_STS;
225 		sci_update(ctx);
226 	}
227 	pthread_mutex_unlock(&pm_lock);
228 }
229 
230 #else
231 /*
232  * Initiate graceful power off.
233  */
234 /*ARGSUSED*/
235 static void
236 power_button_handler(int signal, siginfo_t *type, void *cp)
237 {
238 	/*
239 	 * In theory, taking the 'pm_lock' mutex from within this signal
240 	 * handler could lead to deadlock if the main thread already held this
241 	 * mutex. In reality, this mutex is local to this file and all of the
242 	 * other usage in this file only occurs in functions which are FreeBSD
243 	 * specific (and thus currently not used). Thus, for consistency with
244 	 * the other code in this file, we take the mutex, but in the future,
245 	 * if these other functions are ever enabled for use on non-FreeBSD
246 	 * systems and these functions could be called directly by a thread
247 	 * (which would then hold the mutex), then we need to revisit the use
248 	 * of this mutex in this signal handler.
249 	 */
250 	pthread_mutex_lock(&pm_lock);
251 	if (!(pm1_status & PM1_PWRBTN_STS)) {
252 		pm1_status |= PM1_PWRBTN_STS;
253 		sci_update(pwr_ctx);
254 	}
255 	pthread_mutex_unlock(&pm_lock);
256 }
257 #endif
258 
259 /*
260  * Power Management 1 Control Register
261  *
262  * This is mostly unimplemented except that we wish to handle writes that
263  * set SPL_EN to handle S5 (soft power off).
264  */
265 static uint16_t pm1_control;
266 
267 #define	PM1_SCI_EN	0x0001
268 #define	PM1_SLP_TYP	0x1c00
269 #define	PM1_SLP_EN	0x2000
270 #define	PM1_ALWAYS_ZERO	0xc003
271 
272 static int
273 pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
274     uint32_t *eax, void *arg)
275 {
276 	int error;
277 
278 	if (bytes != 2)
279 		return (-1);
280 	if (in)
281 		*eax = pm1_control;
282 	else {
283 		/*
284 		 * Various bits are write-only or reserved, so force them
285 		 * to zero in pm1_control.  Always preserve SCI_EN as OSPM
286 		 * can never change it.
287 		 */
288 		pm1_control = (pm1_control & PM1_SCI_EN) |
289 		    (*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO));
290 
291 		/*
292 		 * If SLP_EN is set, check for S5.  Bhyve's _S5_ method
293 		 * says that '5' should be stored in SLP_TYP for S5.
294 		 */
295 		if (*eax & PM1_SLP_EN) {
296 			if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) {
297 				error = vm_suspend(ctx, VM_SUSPEND_POWEROFF);
298 				assert(error == 0 || errno == EALREADY);
299 			}
300 		}
301 	}
302 	return (0);
303 }
304 INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
305 #ifdef	__FreeBSD__
306 SYSRES_IO(PM1A_EVT_ADDR, 8);
307 #endif
308 
309 /*
310  * ACPI SMI Command Register
311  *
312  * This write-only register is used to enable and disable ACPI.
313  */
314 static int
315 smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
316     uint32_t *eax, void *arg)
317 {
318 
319 	assert(!in);
320 	if (bytes != 1)
321 		return (-1);
322 
323 	pthread_mutex_lock(&pm_lock);
324 	switch (*eax) {
325 	case BHYVE_ACPI_ENABLE:
326 		pm1_control |= PM1_SCI_EN;
327 #ifdef	__FreeBSD__
328 		if (power_button == NULL) {
329 			power_button = mevent_add(SIGTERM, EVF_SIGNAL,
330 			    power_button_handler, ctx);
331 			old_power_handler = signal(SIGTERM, SIG_IGN);
332 		}
333 #endif
334 		break;
335 	case BHYVE_ACPI_DISABLE:
336 		pm1_control &= ~PM1_SCI_EN;
337 #ifdef	__FreeBSD__
338 		if (power_button != NULL) {
339 			mevent_delete(power_button);
340 			power_button = NULL;
341 			signal(SIGTERM, old_power_handler);
342 		}
343 #endif
344 		break;
345 	}
346 	pthread_mutex_unlock(&pm_lock);
347 	return (0);
348 }
349 INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
350 #ifdef	__FreeBSD__
351 SYSRES_IO(SMI_CMD, 1);
352 #endif
353 
354 void
355 sci_init(struct vmctx *ctx)
356 {
357 
358 	/*
359 	 * Mark ACPI's SCI as level trigger and bump its use count
360 	 * in the PIRQ router.
361 	 */
362 	pci_irq_use(SCI_INT);
363 	vm_isa_set_irq_trigger(ctx, SCI_INT, LEVEL_TRIGGER);
364 
365 #ifndef	__FreeBSD__
366 	{
367 		/*
368 		 * Install SIGTERM signal handler for graceful power off.
369 		 */
370 		struct sigaction act;
371 
372 		pwr_ctx = ctx;
373 		act.sa_flags = 0;
374 		act.sa_sigaction = power_button_handler;
375 		(void) sigaction(SIGTERM, &act, NULL);
376 	}
377 #endif
378 }
379