]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bhyve/pm.c
MFC 263780,264516,265062,265101,265203,265364:
[FreeBSD/stable/10.git] / usr.sbin / bhyve / pm.c
1 /*-
2  * Copyright (c) 2013 Advanced Computing Technologies LLC
3  * Written by: John H. Baldwin <jhb@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/types.h>
32 #include <machine/vmm.h>
33
34 #include <assert.h>
35 #include <pthread.h>
36 #include <signal.h>
37 #include <vmmapi.h>
38
39 #include "acpi.h"
40 #include "inout.h"
41 #include "mevent.h"
42 #include "pci_lpc.h"
43
44 static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
45 static struct mevent *power_button;
46 static sig_t old_power_handler;
47
48 /*
49  * Reset Control register at I/O port 0xcf9.  Bit 2 forces a system
50  * reset when it transitions from 0 to 1.  Bit 1 selects the type of
51  * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
52  * reset.
53  */
54 static int
55 reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
56     uint32_t *eax, void *arg)
57 {
58         static uint8_t reset_control;
59
60         if (bytes != 1)
61                 return (-1);
62         if (in)
63                 *eax = reset_control;
64         else {
65                 reset_control = *eax;
66
67                 /* Treat hard and soft resets the same. */
68                 if (reset_control & 0x4)
69                         return (INOUT_RESET);
70         }
71         return (0);
72 }
73 INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
74
75 /*
76  * ACPI's SCI is a level-triggered interrupt.
77  */
78 static int sci_active;
79
80 static void
81 sci_assert(struct vmctx *ctx)
82 {
83
84         if (sci_active)
85                 return;
86         vm_isa_assert_irq(ctx, SCI_INT, SCI_INT);
87         sci_active = 1;
88 }
89
90 static void
91 sci_deassert(struct vmctx *ctx)
92 {
93
94         if (!sci_active)
95                 return;
96         vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT);
97         sci_active = 0;
98 }
99
100 /*
101  * Power Management 1 Event Registers
102  *
103  * The only power management event supported is a power button upon
104  * receiving SIGTERM.
105  */
106 static uint16_t pm1_enable, pm1_status;
107
108 #define PM1_TMR_STS             0x0001
109 #define PM1_BM_STS              0x0010
110 #define PM1_GBL_STS             0x0020
111 #define PM1_PWRBTN_STS          0x0100
112 #define PM1_SLPBTN_STS          0x0200
113 #define PM1_RTC_STS             0x0400
114 #define PM1_WAK_STS             0x8000
115
116 #define PM1_TMR_EN              0x0001
117 #define PM1_GBL_EN              0x0020
118 #define PM1_PWRBTN_EN           0x0100
119 #define PM1_SLPBTN_EN           0x0200
120 #define PM1_RTC_EN              0x0400
121
122 static void
123 sci_update(struct vmctx *ctx)
124 {
125         int need_sci;
126
127         /* See if the SCI should be active or not. */
128         need_sci = 0;
129         if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS))
130                 need_sci = 1;
131         if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS))
132                 need_sci = 1;
133         if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS))
134                 need_sci = 1;
135         if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS))
136                 need_sci = 1;
137         if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS))
138                 need_sci = 1;
139         if (need_sci)
140                 sci_assert(ctx);
141         else
142                 sci_deassert(ctx);
143 }
144
145 static int
146 pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
147     uint32_t *eax, void *arg)
148 {
149
150         if (bytes != 2)
151                 return (-1);
152
153         pthread_mutex_lock(&pm_lock);
154         if (in)
155                 *eax = pm1_status;
156         else {
157                 /*
158                  * Writes are only permitted to clear certain bits by
159                  * writing 1 to those flags.
160                  */
161                 pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS |
162                     PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS));
163                 sci_update(ctx);
164         }
165         pthread_mutex_unlock(&pm_lock);
166         return (0);
167 }
168
169 static int
170 pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
171     uint32_t *eax, void *arg)
172 {
173
174         if (bytes != 2)
175                 return (-1);
176
177         pthread_mutex_lock(&pm_lock);
178         if (in)
179                 *eax = pm1_enable;
180         else {
181                 /*
182                  * Only permit certain bits to be set.  We never use
183                  * the global lock, but ACPI-CA whines profusely if it
184                  * can't set GBL_EN.
185                  */
186                 pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN);
187                 sci_update(ctx);
188         }
189         pthread_mutex_unlock(&pm_lock);
190         return (0);
191 }
192 INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
193 INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
194
195 static void
196 power_button_handler(int signal, enum ev_type type, void *arg)
197 {
198         struct vmctx *ctx;
199
200         ctx = arg;
201         pthread_mutex_lock(&pm_lock);
202         if (!(pm1_status & PM1_PWRBTN_STS)) {
203                 pm1_status |= PM1_PWRBTN_STS;
204                 sci_update(ctx);
205         }
206         pthread_mutex_unlock(&pm_lock);
207 }
208
209 /*
210  * Power Management 1 Control Register
211  *
212  * This is mostly unimplemented except that we wish to handle writes that
213  * set SPL_EN to handle S5 (soft power off).
214  */
215 static uint16_t pm1_control;
216
217 #define PM1_SCI_EN      0x0001
218 #define PM1_SLP_TYP     0x1c00
219 #define PM1_SLP_EN      0x2000
220 #define PM1_ALWAYS_ZERO 0xc003
221
222 static int
223 pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
224     uint32_t *eax, void *arg)
225 {
226
227         if (bytes != 2)
228                 return (-1);
229         if (in)
230                 *eax = pm1_control;
231         else {
232                 /*
233                  * Various bits are write-only or reserved, so force them
234                  * to zero in pm1_control.  Always preserve SCI_EN as OSPM
235                  * can never change it.
236                  */
237                 pm1_control = (pm1_control & PM1_SCI_EN) |
238                     (*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO));
239
240                 /*
241                  * If SLP_EN is set, check for S5.  Bhyve's _S5_ method
242                  * says that '5' should be stored in SLP_TYP for S5.
243                  */
244                 if (*eax & PM1_SLP_EN) {
245                         if ((pm1_control & PM1_SLP_TYP) >> 10 == 5)
246                                 return (INOUT_POWEROFF);
247                 }
248         }
249         return (0);
250 }
251 INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
252 SYSRES_IO(PM1A_EVT_ADDR, 8);
253
254 /*
255  * ACPI SMI Command Register
256  *
257  * This write-only register is used to enable and disable ACPI.
258  */
259 static int
260 smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
261     uint32_t *eax, void *arg)
262 {
263
264         assert(!in);
265         if (bytes != 1)
266                 return (-1);
267
268         pthread_mutex_lock(&pm_lock);
269         switch (*eax) {
270         case BHYVE_ACPI_ENABLE:
271                 pm1_control |= PM1_SCI_EN;
272                 if (power_button == NULL) {
273                         power_button = mevent_add(SIGTERM, EVF_SIGNAL,
274                             power_button_handler, ctx);
275                         old_power_handler = signal(SIGTERM, SIG_IGN);
276                 }
277                 break;
278         case BHYVE_ACPI_DISABLE:
279                 pm1_control &= ~PM1_SCI_EN;
280                 if (power_button != NULL) {
281                         mevent_delete(power_button);
282                         power_button = NULL;
283                         signal(SIGTERM, old_power_handler);
284                 }
285                 break;
286         }
287         pthread_mutex_unlock(&pm_lock);
288         return (0);
289 }
290 INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
291 SYSRES_IO(SMI_CMD, 1);