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