1 /*-
2 * Copyright (c) 2013 Hudson River Trading 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 #include <sys/types.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <pthread.h>
33 #include <signal.h>
34 #include <stdbool.h>
35 #include <pthread.h>
36
37 #include "vmmapi.h"
38 #include "acpi.h"
39 #include "inout.h"
40 #include "mevent.h"
41 #include "irq.h"
42 #include "lpc.h"
43 #include "monitor.h"
44 #include "log.h"
45 #include "vm_event.h"
46
47 static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
48 static struct mevent *power_button;
49 static sig_t old_power_handler;
50
51 /*
52 * Reset Control register at I/O port 0xcf9. Bit 2 forces a system
53 * reset when it transitions from 0 to 1. Bit 1 selects the type of
54 * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
55 * reset.
56 */
57 static int
reset_handler(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)58 reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
59 uint32_t *eax, void *arg)
60 {
61 static uint8_t reset_control;
62
63 if (bytes != 1)
64 return -1;
65 if (in)
66 *eax = reset_control;
67 else {
68 reset_control = *eax;
69
70 if (*eax & 0x8) {
71 pr_notice("full reset\r\n");
72 vm_suspend(ctx, VM_SUSPEND_FULL_RESET);
73 mevent_notify();
74 reset_control = 0;
75 } else if (*eax & 0x4) {
76 pr_notice("system reset\r\n");
77 vm_suspend(ctx, VM_SUSPEND_SYSTEM_RESET);
78 mevent_notify();
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
sci_assert(struct vmctx * ctx)91 sci_assert(struct vmctx *ctx)
92 {
93 if (sci_active)
94 return;
95 vm_set_gsi_irq(ctx, SCI_INT, GSI_SET_HIGH);
96 sci_active = 1;
97 }
98
99 static void
sci_deassert(struct vmctx * ctx)100 sci_deassert(struct vmctx *ctx)
101 {
102 if (!sci_active)
103 return;
104 vm_set_gsi_irq(ctx, SCI_INT, GSI_SET_LOW);
105 sci_active = 0;
106 }
107
108 /*
109 * Power Management 1 Event Registers
110 *
111 * The only power management event supported is a power button upon
112 * receiving SIGTERM.
113 */
114 static uint16_t pm1_enable, pm1_status;
115
116 #define PM1_TMR_STS 0x0001
117 #define PM1_BM_STS 0x0010
118 #define PM1_GBL_STS 0x0020
119 #define PM1_PWRBTN_STS 0x0100
120 #define PM1_SLPBTN_STS 0x0200
121 #define PM1_RTC_STS 0x0400
122 #define PM1_WAK_STS 0x8000
123
124 #define PM1_TMR_EN 0x0001
125 #define PM1_GBL_EN 0x0020
126 #define PM1_PWRBTN_EN 0x0100
127 #define PM1_SLPBTN_EN 0x0200
128 #define PM1_RTC_EN 0x0400
129
130 /*
131 * Power Management 1 Control Register
132 *
133 * This is mostly unimplemented except that we wish to handle writes that
134 * set SPL_EN to handle S5 (soft power off).
135 */
136 static uint16_t pm1_control;
137
138 static void
sci_update(struct vmctx * ctx)139 sci_update(struct vmctx *ctx)
140 {
141 int need_sci;
142
143 /*
144 * Followed ACPI spec, should trigger SMI if SCI_EN is zero.
145 * Return directly due to ACRN do not support SMI so far.
146 */
147 if (!(pm1_control & VIRTUAL_PM1A_SCI_EN))
148 return;
149
150 /* See if the SCI should be active or not. */
151 need_sci = 0;
152 if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS))
153 need_sci = 1;
154 if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS))
155 need_sci = 1;
156 if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS))
157 need_sci = 1;
158 if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS))
159 need_sci = 1;
160 if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS))
161 need_sci = 1;
162 if (need_sci)
163 sci_assert(ctx);
164 else
165 sci_deassert(ctx);
166 }
167
168 static int
pm1_status_handler(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)169 pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
170 uint32_t *eax, void *arg)
171 {
172 if (bytes != 2)
173 return -1;
174
175 pthread_mutex_lock(&pm_lock);
176 if (in)
177 *eax = pm1_status;
178 else {
179 /*
180 * Writes are only permitted to clear certain bits by
181 * writing 1 to those flags.
182 */
183 pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS |
184 PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS));
185 sci_update(ctx);
186 }
187 pthread_mutex_unlock(&pm_lock);
188
189 return 0;
190 }
191
192 void
pm_backto_wakeup(struct vmctx * ctx)193 pm_backto_wakeup(struct vmctx *ctx)
194 {
195 /* According to ACPI 5.0 Table 4-16: bit 15, WAK_STS should be
196 * set when system transition to the working state
197 */
198 pm1_status |= PM1_WAK_STS;
199 }
200
201 static int
pm1_enable_handler(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)202 pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
203 uint32_t *eax, void *arg)
204 {
205 if (bytes != 2)
206 return -1;
207
208 pthread_mutex_lock(&pm_lock);
209 if (in)
210 *eax = pm1_enable;
211 else {
212 /*
213 * Only permit certain bits to be set. We never use
214 * the global lock, but ACPI-CA whines profusely if it
215 * can't set GBL_EN.
216 */
217 pm1_enable = *eax & (PM1_RTC_EN | PM1_PWRBTN_EN | PM1_GBL_EN);
218 sci_update(ctx);
219 }
220 pthread_mutex_unlock(&pm_lock);
221
222 return 0;
223 }
224 INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
225 INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
226
227 void
inject_power_button_event(struct vmctx * ctx)228 inject_power_button_event(struct vmctx *ctx)
229 {
230 pr_info("press power button\n");
231 pthread_mutex_lock(&pm_lock);
232 if (!(pm1_status & PM1_PWRBTN_STS)) {
233 pm1_status |= PM1_PWRBTN_STS;
234 sci_update(ctx);
235 }
236 pthread_mutex_unlock(&pm_lock);
237 }
238
239 static void
power_button_handler(int signal,enum ev_type type,void * arg)240 power_button_handler(int signal, enum ev_type type, void *arg)
241 {
242 if (arg)
243 inject_power_button_event(arg);
244 }
245
246 static void
send_poweroff_event(void)247 send_poweroff_event(void)
248 {
249 struct vm_event event;
250 event.type = VM_EVENT_POWEROFF;
251 dm_send_vm_event(&event);
252 }
253
254 static int
pm1_control_handler(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)255 pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
256 uint32_t *eax, void *arg)
257 {
258 if (bytes != 2)
259 return -1;
260 if (in)
261 *eax = pm1_control;
262 else {
263 /*
264 * Various bits are write-only or reserved, so force them
265 * to zero in pm1_control. Always preserve SCI_EN as OSPM
266 * can never change it.
267 */
268 pm1_control = (pm1_control & VIRTUAL_PM1A_SCI_EN) |
269 (*eax & ~(VIRTUAL_PM1A_SLP_EN | VIRTUAL_PM1A_ALWAYS_ZERO));
270
271 /*
272 * If SLP_EN is set, check for S5. ACRN-DM's _S5_ method
273 * says that '5' should be stored in SLP_TYP for S5.
274 */
275 if (*eax & VIRTUAL_PM1A_SLP_EN) {
276 if ((pm1_control & VIRTUAL_PM1A_SLP_TYP) >> 10 == 5) {
277 send_poweroff_event();
278 vm_suspend(ctx, VM_SUSPEND_POWEROFF);
279 }
280
281 if ((pm1_control & VIRTUAL_PM1A_SLP_TYP) >> 10 == 3) {
282 vm_suspend(ctx, VM_SUSPEND_SUSPEND);
283 }
284 }
285 }
286 return 0;
287 }
288 INOUT_PORT(pm1_control, VIRTUAL_PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
289 SYSRES_IO(PM1A_EVT_ADDR, 8);
290
291 /*
292 * ACPI SMI Command Register
293 *
294 * This write-only register is used to enable and disable ACPI.
295 */
296 static int
smi_cmd_handler(struct vmctx * ctx,int vcpu,int in,int port,int bytes,uint32_t * eax,void * arg)297 smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
298 uint32_t *eax, void *arg)
299 {
300 if (in || (bytes != 1))
301 return -1;
302
303 pthread_mutex_lock(&pm_lock);
304 switch (*eax & 0xFF) {
305 case ACPI_ENABLE:
306 pm1_control |= VIRTUAL_PM1A_SCI_EN;
307 /*
308 * FIXME: ACPI_ENABLE/ACPI_DISABLE only impacts SCI_EN via SMI
309 * command register, not impact power button emulation. so need
310 * to remove all power button emulation from here.
311 */
312 if (power_button == NULL) {
313
314 /*
315 * TODO: For the SIGTERM, IOC mediator also needs to
316 * support it, and SIGTERM handler needs to be written
317 * as one common interface for both APCI power button
318 * and IOC mediator in future.
319 */
320 power_button = mevent_add(SIGTERM, EVF_SIGNAL,
321 power_button_handler, ctx, NULL, NULL);
322 old_power_handler = signal(SIGTERM, SIG_IGN);
323 }
324 break;
325 case ACPI_DISABLE:
326 pm1_control &= ~VIRTUAL_PM1A_SCI_EN;
327 if (power_button != NULL) {
328 mevent_delete(power_button);
329 power_button = NULL;
330 signal(SIGTERM, old_power_handler);
331 }
332 break;
333 }
334 pthread_mutex_unlock(&pm_lock);
335 return 0;
336 }
337 INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
338 SYSRES_IO(SMI_CMD, 1);
339
340 void
sci_init(struct vmctx * ctx)341 sci_init(struct vmctx *ctx)
342 {
343 /*
344 * Mark ACPI's SCI as level trigger and bump its use count
345 * in the PIRQ router.
346 */
347 pci_irq_use(SCI_INT);
348 }
349