Extend the ACPI power management support to wire a virtual power button up
to SIGTERM when ACPI is enabled. Sending SIGTERM to the hypervisor when an ACPI-aware OS is running will now trigger a soft-off allowing for a graceful shutdown of the guest. - Move constants for ACPI-related registers to acpi.h. - Implement an SMI_CMD register with commands to enable and disable ACPI. Currently the only change when ACPI is enabled is to enable the virtual power button via SIGTERM. - Implement a fixed-feature power button when ACPI is enabled by asserting PWRBTN_STS in PM1_EVT when SIGTERM is received. - Add support for EVFILT_SIGNAL events to mevent. - Implement support for the ACPI system command interrupt (SCI) and assert it when needed based on the values in PM1_EVT. Mark the SCI as active-low and level triggered in the MADT and MP Table. - Mark PCI interrupts in the MP Table as active-low in addition to level triggered. Reviewed by: neel
This commit is contained in:
parent
cf952fe841
commit
058e24d34b
@ -85,10 +85,6 @@ __FBSDID("$FreeBSD$");
|
|||||||
#define BHYVE_ASL_SUFFIX ".aml"
|
#define BHYVE_ASL_SUFFIX ".aml"
|
||||||
#define BHYVE_ASL_COMPILER "/usr/sbin/iasl"
|
#define BHYVE_ASL_COMPILER "/usr/sbin/iasl"
|
||||||
|
|
||||||
#define BHYVE_PM1A_EVT_ADDR 0x400
|
|
||||||
#define BHYVE_PM1A_CNT_ADDR 0x404
|
|
||||||
#define BHYVE_PM_TIMER_ADDR 0x408
|
|
||||||
|
|
||||||
static int basl_keep_temps;
|
static int basl_keep_temps;
|
||||||
static int basl_verbose_iasl;
|
static int basl_verbose_iasl;
|
||||||
static int basl_ncpu;
|
static int basl_ncpu;
|
||||||
@ -285,11 +281,11 @@ basl_fwrite_madt(FILE *fp)
|
|||||||
EFPRINTF(fp, "[0001]\t\tSubtable Type : 02\n");
|
EFPRINTF(fp, "[0001]\t\tSubtable Type : 02\n");
|
||||||
EFPRINTF(fp, "[0001]\t\tLength : 0A\n");
|
EFPRINTF(fp, "[0001]\t\tLength : 0A\n");
|
||||||
EFPRINTF(fp, "[0001]\t\tBus : 00\n");
|
EFPRINTF(fp, "[0001]\t\tBus : 00\n");
|
||||||
EFPRINTF(fp, "[0001]\t\tSource : 09\n");
|
EFPRINTF(fp, "[0001]\t\tSource : %02X\n", SCI_INT);
|
||||||
EFPRINTF(fp, "[0004]\t\tInterrupt : 00000009\n");
|
EFPRINTF(fp, "[0004]\t\tInterrupt : %08X\n", SCI_INT);
|
||||||
EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0000\n");
|
EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0000\n");
|
||||||
EFPRINTF(fp, "\t\t\tPolarity : 0\n");
|
EFPRINTF(fp, "\t\t\tPolarity : 3\n");
|
||||||
EFPRINTF(fp, "\t\t\tTrigger Mode : 0\n");
|
EFPRINTF(fp, "\t\t\tTrigger Mode : 3\n");
|
||||||
EFPRINTF(fp, "\n");
|
EFPRINTF(fp, "\n");
|
||||||
|
|
||||||
/* Local APIC NMI is connected to LINT 1 on all CPUs */
|
/* Local APIC NMI is connected to LINT 1 on all CPUs */
|
||||||
@ -336,23 +332,27 @@ basl_fwrite_fadt(FILE *fp)
|
|||||||
basl_acpi_base + FACS_OFFSET);
|
basl_acpi_base + FACS_OFFSET);
|
||||||
EFPRINTF(fp, "[0004]\t\tDSDT Address : %08X\n",
|
EFPRINTF(fp, "[0004]\t\tDSDT Address : %08X\n",
|
||||||
basl_acpi_base + DSDT_OFFSET);
|
basl_acpi_base + DSDT_OFFSET);
|
||||||
EFPRINTF(fp, "[0001]\t\tModel : 00\n");
|
EFPRINTF(fp, "[0001]\t\tModel : 01\n");
|
||||||
EFPRINTF(fp, "[0001]\t\tPM Profile : 00 [Unspecified]\n");
|
EFPRINTF(fp, "[0001]\t\tPM Profile : 00 [Unspecified]\n");
|
||||||
EFPRINTF(fp, "[0002]\t\tSCI Interrupt : 0009\n");
|
EFPRINTF(fp, "[0002]\t\tSCI Interrupt : %04X\n",
|
||||||
EFPRINTF(fp, "[0004]\t\tSMI Command Port : 00000000\n");
|
SCI_INT);
|
||||||
EFPRINTF(fp, "[0001]\t\tACPI Enable Value : 00\n");
|
EFPRINTF(fp, "[0004]\t\tSMI Command Port : %08X\n",
|
||||||
EFPRINTF(fp, "[0001]\t\tACPI Disable Value : 00\n");
|
SMI_CMD);
|
||||||
|
EFPRINTF(fp, "[0001]\t\tACPI Enable Value : %02X\n",
|
||||||
|
BHYVE_ACPI_ENABLE);
|
||||||
|
EFPRINTF(fp, "[0001]\t\tACPI Disable Value : %02X\n",
|
||||||
|
BHYVE_ACPI_DISABLE);
|
||||||
EFPRINTF(fp, "[0001]\t\tS4BIOS Command : 00\n");
|
EFPRINTF(fp, "[0001]\t\tS4BIOS Command : 00\n");
|
||||||
EFPRINTF(fp, "[0001]\t\tP-State Control : 00\n");
|
EFPRINTF(fp, "[0001]\t\tP-State Control : 00\n");
|
||||||
EFPRINTF(fp, "[0004]\t\tPM1A Event Block Address : %08X\n",
|
EFPRINTF(fp, "[0004]\t\tPM1A Event Block Address : %08X\n",
|
||||||
BHYVE_PM1A_EVT_ADDR);
|
PM1A_EVT_ADDR);
|
||||||
EFPRINTF(fp, "[0004]\t\tPM1B Event Block Address : 00000000\n");
|
EFPRINTF(fp, "[0004]\t\tPM1B Event Block Address : 00000000\n");
|
||||||
EFPRINTF(fp, "[0004]\t\tPM1A Control Block Address : %08X\n",
|
EFPRINTF(fp, "[0004]\t\tPM1A Control Block Address : %08X\n",
|
||||||
BHYVE_PM1A_CNT_ADDR);
|
PM1A_CNT_ADDR);
|
||||||
EFPRINTF(fp, "[0004]\t\tPM1B Control Block Address : 00000000\n");
|
EFPRINTF(fp, "[0004]\t\tPM1B Control Block Address : 00000000\n");
|
||||||
EFPRINTF(fp, "[0004]\t\tPM2 Control Block Address : 00000000\n");
|
EFPRINTF(fp, "[0004]\t\tPM2 Control Block Address : 00000000\n");
|
||||||
EFPRINTF(fp, "[0004]\t\tPM Timer Block Address : %08X\n",
|
EFPRINTF(fp, "[0004]\t\tPM Timer Block Address : %08X\n",
|
||||||
BHYVE_PM_TIMER_ADDR);
|
IO_PMTMR);
|
||||||
EFPRINTF(fp, "[0004]\t\tGPE0 Block Address : 00000000\n");
|
EFPRINTF(fp, "[0004]\t\tGPE0 Block Address : 00000000\n");
|
||||||
EFPRINTF(fp, "[0004]\t\tGPE1 Block Address : 00000000\n");
|
EFPRINTF(fp, "[0004]\t\tGPE1 Block Address : 00000000\n");
|
||||||
EFPRINTF(fp, "[0001]\t\tPM1 Event Block Length : 04\n");
|
EFPRINTF(fp, "[0001]\t\tPM1 Event Block Length : 04\n");
|
||||||
@ -385,7 +385,7 @@ basl_fwrite_fadt(FILE *fp)
|
|||||||
EFPRINTF(fp, "\t\t\tWBINVD flushes all caches (V1) : 0\n");
|
EFPRINTF(fp, "\t\t\tWBINVD flushes all caches (V1) : 0\n");
|
||||||
EFPRINTF(fp, "\t\t\tAll CPUs support C1 (V1) : 1\n");
|
EFPRINTF(fp, "\t\t\tAll CPUs support C1 (V1) : 1\n");
|
||||||
EFPRINTF(fp, "\t\t\tC2 works on MP system (V1) : 0\n");
|
EFPRINTF(fp, "\t\t\tC2 works on MP system (V1) : 0\n");
|
||||||
EFPRINTF(fp, "\t\t\tControl Method Power Button (V1) : 1\n");
|
EFPRINTF(fp, "\t\t\tControl Method Power Button (V1) : 0\n");
|
||||||
EFPRINTF(fp, "\t\t\tControl Method Sleep Button (V1) : 1\n");
|
EFPRINTF(fp, "\t\t\tControl Method Sleep Button (V1) : 1\n");
|
||||||
EFPRINTF(fp, "\t\t\tRTC wake not in fixed reg space (V1) : 0\n");
|
EFPRINTF(fp, "\t\t\tRTC wake not in fixed reg space (V1) : 0\n");
|
||||||
EFPRINTF(fp, "\t\t\tRTC can wake system from S4 (V1) : 0\n");
|
EFPRINTF(fp, "\t\t\tRTC can wake system from S4 (V1) : 0\n");
|
||||||
@ -427,7 +427,7 @@ basl_fwrite_fadt(FILE *fp)
|
|||||||
EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
|
EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
|
||||||
EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
|
EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
|
||||||
EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
|
EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
|
||||||
BHYVE_PM1A_EVT_ADDR);
|
PM1A_EVT_ADDR);
|
||||||
EFPRINTF(fp, "\n");
|
EFPRINTF(fp, "\n");
|
||||||
|
|
||||||
EFPRINTF(fp,
|
EFPRINTF(fp,
|
||||||
@ -447,7 +447,7 @@ basl_fwrite_fadt(FILE *fp)
|
|||||||
EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
|
EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
|
||||||
EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
|
EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
|
||||||
EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
|
EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
|
||||||
BHYVE_PM1A_CNT_ADDR);
|
PM1A_CNT_ADDR);
|
||||||
EFPRINTF(fp, "\n");
|
EFPRINTF(fp, "\n");
|
||||||
|
|
||||||
EFPRINTF(fp,
|
EFPRINTF(fp,
|
||||||
@ -479,7 +479,7 @@ basl_fwrite_fadt(FILE *fp)
|
|||||||
EFPRINTF(fp,
|
EFPRINTF(fp,
|
||||||
"[0001]\t\tEncoded Access Width : 03 [DWord Access:32]\n");
|
"[0001]\t\tEncoded Access Width : 03 [DWord Access:32]\n");
|
||||||
EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
|
EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
|
||||||
BHYVE_PM_TIMER_ADDR);
|
IO_PMTMR);
|
||||||
EFPRINTF(fp, "\n");
|
EFPRINTF(fp, "\n");
|
||||||
|
|
||||||
EFPRINTF(fp, "[0012]\t\tGPE0 Block : [Generic Address Structure]\n");
|
EFPRINTF(fp, "[0012]\t\tGPE0 Block : [Generic Address Structure]\n");
|
||||||
|
@ -29,6 +29,19 @@
|
|||||||
#ifndef _ACPI_H_
|
#ifndef _ACPI_H_
|
||||||
#define _ACPI_H_
|
#define _ACPI_H_
|
||||||
|
|
||||||
|
#define SCI_INT 9
|
||||||
|
|
||||||
|
#define SMI_CMD 0xb2
|
||||||
|
#define BHYVE_ACPI_ENABLE 0xa0
|
||||||
|
#define BHYVE_ACPI_DISABLE 0xa1
|
||||||
|
|
||||||
|
#define PM1A_EVT_ADDR 0x400
|
||||||
|
#define PM1A_CNT_ADDR 0x404
|
||||||
|
|
||||||
|
#define IO_PMTMR 0x408 /* 4-byte i/o port for the timer */
|
||||||
|
|
||||||
|
struct vmctx;
|
||||||
|
|
||||||
int acpi_build(struct vmctx *ctx, int ncpu);
|
int acpi_build(struct vmctx *ctx, int ncpu);
|
||||||
|
|
||||||
#endif /* _ACPI_H_ */
|
#endif /* _ACPI_H_ */
|
||||||
|
@ -135,6 +135,9 @@ mevent_kq_filter(struct mevent *mevp)
|
|||||||
if (mevp->me_type == EVF_TIMER)
|
if (mevp->me_type == EVF_TIMER)
|
||||||
retval = EVFILT_TIMER;
|
retval = EVFILT_TIMER;
|
||||||
|
|
||||||
|
if (mevp->me_type == EVF_SIGNAL)
|
||||||
|
retval = EVFILT_SIGNAL;
|
||||||
|
|
||||||
return (retval);
|
return (retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,7 +440,7 @@ mevent_dispatch(void)
|
|||||||
* Block awaiting events
|
* Block awaiting events
|
||||||
*/
|
*/
|
||||||
ret = kevent(mfd, NULL, 0, eventlist, MEVENT_MAX, NULL);
|
ret = kevent(mfd, NULL, 0, eventlist, MEVENT_MAX, NULL);
|
||||||
if (ret == -1) {
|
if (ret == -1 && errno != EINTR) {
|
||||||
perror("Error return from kevent monitor");
|
perror("Error return from kevent monitor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
enum ev_type {
|
enum ev_type {
|
||||||
EVF_READ,
|
EVF_READ,
|
||||||
EVF_WRITE,
|
EVF_WRITE,
|
||||||
EVF_TIMER
|
EVF_TIMER,
|
||||||
|
EVF_SIGNAL
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mevent;
|
struct mevent;
|
||||||
|
@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "acpi.h"
|
||||||
#include "bhyverun.h"
|
#include "bhyverun.h"
|
||||||
#include "mptbl.h"
|
#include "mptbl.h"
|
||||||
|
|
||||||
@ -227,13 +228,21 @@ mpt_build_ioint_entries(int_entry_ptr mpie, int num_pins, int id)
|
|||||||
mpie->int_type = INTENTRY_TYPE_INT;
|
mpie->int_type = INTENTRY_TYPE_INT;
|
||||||
mpie->src_bus_irq = 0;
|
mpie->src_bus_irq = 0;
|
||||||
break;
|
break;
|
||||||
|
case SCI_INT:
|
||||||
|
/* ACPI SCI is level triggered and active-lo. */
|
||||||
|
mpie->int_flags = INTENTRY_FLAGS_POLARITY_ACTIVELO |
|
||||||
|
INTENTRY_FLAGS_TRIGGER_LEVEL;
|
||||||
|
mpie->int_type = INTENTRY_TYPE_INT;
|
||||||
|
mpie->src_bus_irq = SCI_INT;
|
||||||
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
case 10:
|
case 10:
|
||||||
case 11:
|
case 11:
|
||||||
/*
|
/*
|
||||||
* PCI Irqs set to level triggered.
|
* PCI Irqs set to level triggered and active-lo.
|
||||||
*/
|
*/
|
||||||
mpie->int_flags = INTENTRY_FLAGS_TRIGGER_LEVEL;
|
mpie->int_flags = INTENTRY_FLAGS_POLARITY_ACTIVELO |
|
||||||
|
INTENTRY_FLAGS_TRIGGER_LEVEL;
|
||||||
mpie->src_bus_id = 0;
|
mpie->src_bus_id = 0;
|
||||||
/* fall through.. */
|
/* fall through.. */
|
||||||
default:
|
default:
|
||||||
|
@ -29,11 +29,20 @@
|
|||||||
__FBSDID("$FreeBSD$");
|
__FBSDID("$FreeBSD$");
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <machine/vmm.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <vmmapi.h>
|
||||||
|
|
||||||
|
#include "acpi.h"
|
||||||
#include "inout.h"
|
#include "inout.h"
|
||||||
|
#include "mevent.h"
|
||||||
|
|
||||||
#define PM1A_EVT_ADDR 0x400
|
static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
#define PM1A_CNT_ADDR 0x404
|
static struct mevent *power_button;
|
||||||
|
static sig_t old_power_handler;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset Control register at I/O port 0xcf9. Bit 2 forces a system
|
* Reset Control register at I/O port 0xcf9. Bit 2 forces a system
|
||||||
@ -62,13 +71,76 @@ reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
|||||||
}
|
}
|
||||||
INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
|
INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ACPI's SCI is a level-triggered interrupt.
|
||||||
|
*/
|
||||||
|
static int sci_active;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sci_assert(struct vmctx *ctx)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (sci_active)
|
||||||
|
return;
|
||||||
|
vm_ioapic_assert_irq(ctx, SCI_INT);
|
||||||
|
sci_active = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sci_deassert(struct vmctx *ctx)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!sci_active)
|
||||||
|
return;
|
||||||
|
vm_ioapic_deassert_irq(ctx, SCI_INT);
|
||||||
|
sci_active = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Power Management 1 Event Registers
|
* Power Management 1 Event Registers
|
||||||
*
|
*
|
||||||
* bhyve doesn't support any power management events currently, so the
|
* The only power management event supported is a power button upon
|
||||||
* status register always returns zero. The enable register preserves
|
* receiving SIGTERM.
|
||||||
* its value but has no effect.
|
|
||||||
*/
|
*/
|
||||||
|
static uint16_t pm1_enable, pm1_status;
|
||||||
|
|
||||||
|
#define PM1_TMR_STS 0x0001
|
||||||
|
#define PM1_BM_STS 0x0010
|
||||||
|
#define PM1_GBL_STS 0x0020
|
||||||
|
#define PM1_PWRBTN_STS 0x0100
|
||||||
|
#define PM1_SLPBTN_STS 0x0200
|
||||||
|
#define PM1_RTC_STS 0x0400
|
||||||
|
#define PM1_WAK_STS 0x8000
|
||||||
|
|
||||||
|
#define PM1_TMR_EN 0x0001
|
||||||
|
#define PM1_GBL_EN 0x0020
|
||||||
|
#define PM1_PWRBTN_EN 0x0100
|
||||||
|
#define PM1_SLPBTN_EN 0x0200
|
||||||
|
#define PM1_RTC_EN 0x0400
|
||||||
|
|
||||||
|
static void
|
||||||
|
sci_update(struct vmctx *ctx)
|
||||||
|
{
|
||||||
|
int need_sci;
|
||||||
|
|
||||||
|
/* See if the SCI should be active or not. */
|
||||||
|
need_sci = 0;
|
||||||
|
if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS))
|
||||||
|
need_sci = 1;
|
||||||
|
if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS))
|
||||||
|
need_sci = 1;
|
||||||
|
if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS))
|
||||||
|
need_sci = 1;
|
||||||
|
if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS))
|
||||||
|
need_sci = 1;
|
||||||
|
if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS))
|
||||||
|
need_sci = 1;
|
||||||
|
if (need_sci)
|
||||||
|
sci_assert(ctx);
|
||||||
|
else
|
||||||
|
sci_deassert(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||||
uint32_t *eax, void *arg)
|
uint32_t *eax, void *arg)
|
||||||
@ -76,8 +148,20 @@ pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
|||||||
|
|
||||||
if (bytes != 2)
|
if (bytes != 2)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&pm_lock);
|
||||||
if (in)
|
if (in)
|
||||||
*eax = 0;
|
*eax = pm1_status;
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
* Writes are only permitted to clear certain bits by
|
||||||
|
* writing 1 to those flags.
|
||||||
|
*/
|
||||||
|
pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS |
|
||||||
|
PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS));
|
||||||
|
sci_update(ctx);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pm_lock);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,25 +169,51 @@ static int
|
|||||||
pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||||
uint32_t *eax, void *arg)
|
uint32_t *eax, void *arg)
|
||||||
{
|
{
|
||||||
static uint16_t pm1_enable;
|
|
||||||
|
|
||||||
if (bytes != 2)
|
if (bytes != 2)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&pm_lock);
|
||||||
if (in)
|
if (in)
|
||||||
*eax = pm1_enable;
|
*eax = pm1_enable;
|
||||||
else
|
else {
|
||||||
pm1_enable = *eax;
|
/*
|
||||||
|
* Only permit certain bits to be set. We never use
|
||||||
|
* the global lock, but ACPI-CA whines profusely if it
|
||||||
|
* can't set GBL_EN.
|
||||||
|
*/
|
||||||
|
pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN);
|
||||||
|
sci_update(ctx);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pm_lock);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
|
INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
|
||||||
INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
|
INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
|
||||||
|
|
||||||
|
static void
|
||||||
|
power_button_handler(int signal, enum ev_type type, void *arg)
|
||||||
|
{
|
||||||
|
struct vmctx *ctx;
|
||||||
|
|
||||||
|
ctx = arg;
|
||||||
|
pthread_mutex_lock(&pm_lock);
|
||||||
|
if (!(pm1_status & PM1_PWRBTN_STS)) {
|
||||||
|
pm1_status |= PM1_PWRBTN_STS;
|
||||||
|
sci_update(ctx);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pm_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Power Management 1 Control Register
|
* Power Management 1 Control Register
|
||||||
*
|
*
|
||||||
* This is mostly unimplemented except that we wish to handle writes that
|
* This is mostly unimplemented except that we wish to handle writes that
|
||||||
* set SPL_EN to handle S5 (soft power off).
|
* set SPL_EN to handle S5 (soft power off).
|
||||||
*/
|
*/
|
||||||
|
static uint16_t pm1_control;
|
||||||
|
|
||||||
|
#define PM1_SCI_EN 0x0001
|
||||||
#define PM1_SLP_TYP 0x1c00
|
#define PM1_SLP_TYP 0x1c00
|
||||||
#define PM1_SLP_EN 0x2000
|
#define PM1_SLP_EN 0x2000
|
||||||
#define PM1_ALWAYS_ZERO 0xc003
|
#define PM1_ALWAYS_ZERO 0xc003
|
||||||
@ -112,7 +222,6 @@ static int
|
|||||||
pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||||
uint32_t *eax, void *arg)
|
uint32_t *eax, void *arg)
|
||||||
{
|
{
|
||||||
static uint16_t pm1_control;
|
|
||||||
|
|
||||||
if (bytes != 2)
|
if (bytes != 2)
|
||||||
return (-1);
|
return (-1);
|
||||||
@ -121,9 +230,11 @@ pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
|||||||
else {
|
else {
|
||||||
/*
|
/*
|
||||||
* Various bits are write-only or reserved, so force them
|
* Various bits are write-only or reserved, so force them
|
||||||
* to zero in pm1_control.
|
* to zero in pm1_control. Always preserve SCI_EN as OSPM
|
||||||
|
* can never change it.
|
||||||
*/
|
*/
|
||||||
pm1_control = *eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO);
|
pm1_control = (pm1_control & PM1_SCI_EN) |
|
||||||
|
(*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If SLP_EN is set, check for S5. Bhyve's _S5_ method
|
* If SLP_EN is set, check for S5. Bhyve's _S5_ method
|
||||||
@ -137,3 +248,41 @@ pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
|
INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ACPI SMI Command Register
|
||||||
|
*
|
||||||
|
* This write-only register is used to enable and disable ACPI.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||||
|
uint32_t *eax, void *arg)
|
||||||
|
{
|
||||||
|
|
||||||
|
assert(!in);
|
||||||
|
if (bytes != 1)
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&pm_lock);
|
||||||
|
switch (*eax) {
|
||||||
|
case BHYVE_ACPI_ENABLE:
|
||||||
|
pm1_control |= PM1_SCI_EN;
|
||||||
|
if (power_button == NULL) {
|
||||||
|
power_button = mevent_add(SIGTERM, EVF_SIGNAL,
|
||||||
|
power_button_handler, ctx);
|
||||||
|
old_power_handler = signal(SIGTERM, SIG_IGN);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BHYVE_ACPI_DISABLE:
|
||||||
|
pm1_control &= ~PM1_SCI_EN;
|
||||||
|
if (power_button != NULL) {
|
||||||
|
mevent_delete(power_button);
|
||||||
|
power_button = NULL;
|
||||||
|
signal(SIGTERM, old_power_handler);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pm_lock);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
|
||||||
|
@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "acpi.h"
|
||||||
#include "inout.h"
|
#include "inout.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -49,8 +50,6 @@ __FBSDID("$FreeBSD$");
|
|||||||
* This implementation will be 32-bits
|
* This implementation will be 32-bits
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define IO_PMTMR 0x408 /* 4-byte i/o port for the timer */
|
|
||||||
|
|
||||||
#define PMTMR_FREQ 3579545 /* 3.579545MHz */
|
#define PMTMR_FREQ 3579545 /* 3.579545MHz */
|
||||||
|
|
||||||
static pthread_mutex_t pmtmr_mtx;
|
static pthread_mutex_t pmtmr_mtx;
|
||||||
|
Loading…
Reference in New Issue
Block a user