Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 28 Dec 2013 04:01:05 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r259998 - head/usr.sbin/bhyve
Message-ID:  <201312280401.rBS415Uu081102@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Sat Dec 28 04:01:05 2013
New Revision: 259998
URL: http://svnweb.freebsd.org/changeset/base/259998

Log:
  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

Modified:
  head/usr.sbin/bhyve/acpi.c
  head/usr.sbin/bhyve/acpi.h
  head/usr.sbin/bhyve/mevent.c
  head/usr.sbin/bhyve/mevent.h
  head/usr.sbin/bhyve/mptbl.c
  head/usr.sbin/bhyve/pm.c
  head/usr.sbin/bhyve/pmtmr.c

Modified: head/usr.sbin/bhyve/acpi.c
==============================================================================
--- head/usr.sbin/bhyve/acpi.c	Sat Dec 28 03:21:15 2013	(r259997)
+++ head/usr.sbin/bhyve/acpi.c	Sat Dec 28 04:01:05 2013	(r259998)
@@ -85,10 +85,6 @@ __FBSDID("$FreeBSD$");
 #define BHYVE_ASL_SUFFIX	".aml"
 #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_verbose_iasl;
 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\tLength : 0A\n");
 	EFPRINTF(fp, "[0001]\t\tBus : 00\n");
-	EFPRINTF(fp, "[0001]\t\tSource : 09\n");
-	EFPRINTF(fp, "[0004]\t\tInterrupt : 00000009\n");
+	EFPRINTF(fp, "[0001]\t\tSource : %02X\n", SCI_INT);
+	EFPRINTF(fp, "[0004]\t\tInterrupt : %08X\n", SCI_INT);
 	EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0000\n");
-	EFPRINTF(fp, "\t\t\tPolarity : 0\n");
-	EFPRINTF(fp, "\t\t\tTrigger Mode : 0\n");
+	EFPRINTF(fp, "\t\t\tPolarity : 3\n");
+	EFPRINTF(fp, "\t\t\tTrigger Mode : 3\n");
 	EFPRINTF(fp, "\n");
 
 	/* 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);
 	EFPRINTF(fp, "[0004]\t\tDSDT Address : %08X\n",
 	    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, "[0002]\t\tSCI Interrupt : 0009\n");
-	EFPRINTF(fp, "[0004]\t\tSMI Command Port : 00000000\n");
-	EFPRINTF(fp, "[0001]\t\tACPI Enable Value : 00\n");
-	EFPRINTF(fp, "[0001]\t\tACPI Disable Value : 00\n");
+	EFPRINTF(fp, "[0002]\t\tSCI Interrupt : %04X\n",
+	    SCI_INT);
+	EFPRINTF(fp, "[0004]\t\tSMI Command Port : %08X\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\tP-State Control : 00\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\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\tPM2 Control Block Address : 00000000\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\tGPE1 Block Address : 00000000\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\tAll CPUs support C1 (V1) : 1\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\tRTC wake not in fixed reg space (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\tEncoded Access Width : 02 [Word Access:16]\n");
 	EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
-	    BHYVE_PM1A_EVT_ADDR);
+	    PM1A_EVT_ADDR);
 	EFPRINTF(fp, "\n");
 	
 	EFPRINTF(fp,
@@ -447,7 +447,7 @@ basl_fwrite_fadt(FILE *fp)
 	EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
 	EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
 	EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
-	    BHYVE_PM1A_CNT_ADDR);
+	    PM1A_CNT_ADDR);
 	EFPRINTF(fp, "\n");
 
 	EFPRINTF(fp,
@@ -479,7 +479,7 @@ basl_fwrite_fadt(FILE *fp)
 	EFPRINTF(fp,
 	    "[0001]\t\tEncoded Access Width : 03 [DWord Access:32]\n");
 	EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
-	    BHYVE_PM_TIMER_ADDR);
+	    IO_PMTMR);
 	EFPRINTF(fp, "\n");
 
 	EFPRINTF(fp, "[0012]\t\tGPE0 Block : [Generic Address Structure]\n");

Modified: head/usr.sbin/bhyve/acpi.h
==============================================================================
--- head/usr.sbin/bhyve/acpi.h	Sat Dec 28 03:21:15 2013	(r259997)
+++ head/usr.sbin/bhyve/acpi.h	Sat Dec 28 04:01:05 2013	(r259998)
@@ -29,6 +29,19 @@
 #ifndef _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);
 
 #endif /* _ACPI_H_ */

Modified: head/usr.sbin/bhyve/mevent.c
==============================================================================
--- head/usr.sbin/bhyve/mevent.c	Sat Dec 28 03:21:15 2013	(r259997)
+++ head/usr.sbin/bhyve/mevent.c	Sat Dec 28 04:01:05 2013	(r259998)
@@ -135,6 +135,9 @@ mevent_kq_filter(struct mevent *mevp)
 	if (mevp->me_type == EVF_TIMER)
 		retval = EVFILT_TIMER;
 
+	if (mevp->me_type == EVF_SIGNAL)
+		retval = EVFILT_SIGNAL;
+
 	return (retval);
 }
 
@@ -437,7 +440,7 @@ mevent_dispatch(void)
 		 * Block awaiting events
 		 */
 		ret = kevent(mfd, NULL, 0, eventlist, MEVENT_MAX, NULL);
-		if (ret == -1) {
+		if (ret == -1 && errno != EINTR) {
 			perror("Error return from kevent monitor");
 		}
 		

Modified: head/usr.sbin/bhyve/mevent.h
==============================================================================
--- head/usr.sbin/bhyve/mevent.h	Sat Dec 28 03:21:15 2013	(r259997)
+++ head/usr.sbin/bhyve/mevent.h	Sat Dec 28 04:01:05 2013	(r259998)
@@ -32,7 +32,8 @@
 enum ev_type {
 	EVF_READ,
 	EVF_WRITE,
-	EVF_TIMER
+	EVF_TIMER,
+	EVF_SIGNAL
 };
 
 struct mevent;

Modified: head/usr.sbin/bhyve/mptbl.c
==============================================================================
--- head/usr.sbin/bhyve/mptbl.c	Sat Dec 28 03:21:15 2013	(r259997)
+++ head/usr.sbin/bhyve/mptbl.c	Sat Dec 28 04:01:05 2013	(r259998)
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
 #include <stdio.h>
 #include <string.h>
 
+#include "acpi.h"
 #include "bhyverun.h"
 #include "mptbl.h"
 
@@ -227,13 +228,21 @@ mpt_build_ioint_entries(int_entry_ptr mp
 			mpie->int_type = INTENTRY_TYPE_INT;
 			mpie->src_bus_irq = 0;
 			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 10:
 		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;
 			/* fall through.. */
 		default:

Modified: head/usr.sbin/bhyve/pm.c
==============================================================================
--- head/usr.sbin/bhyve/pm.c	Sat Dec 28 03:21:15 2013	(r259997)
+++ head/usr.sbin/bhyve/pm.c	Sat Dec 28 04:01:05 2013	(r259998)
@@ -29,11 +29,20 @@
 __FBSDID("$FreeBSD$");
 
 #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 "mevent.h"
 
-#define	PM1A_EVT_ADDR	0x400
-#define	PM1A_CNT_ADDR	0x404
+static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct mevent *power_button;
+static sig_t old_power_handler;
 
 /*
  * Reset Control register at I/O port 0xcf9.  Bit 2 forces a system
@@ -63,12 +72,75 @@ reset_handler(struct vmctx *ctx, int vcp
 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
  *
- * bhyve doesn't support any power management events currently, so the
- * status register always returns zero.  The enable register preserves
- * its value but has no effect.
+ * The only power management event supported is a power button upon
+ * receiving SIGTERM.
  */
+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
 pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
     uint32_t *eax, void *arg)
@@ -76,8 +148,20 @@ pm1_status_handler(struct vmctx *ctx, in
 
 	if (bytes != 2)
 		return (-1);
+
+	pthread_mutex_lock(&pm_lock);
 	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);
 }
 
@@ -85,25 +169,51 @@ static int
 pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
     uint32_t *eax, void *arg)
 {
-	static uint16_t pm1_enable;
 
 	if (bytes != 2)
 		return (-1);
+
+	pthread_mutex_lock(&pm_lock);
 	if (in)
 		*eax = pm1_enable;
-	else
-		pm1_enable = *eax;
+	else {
+		/*
+		 * 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);
 }
 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);
 
+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
  *
  * This is mostly unimplemented except that we wish to handle writes that
  * 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_EN	0x2000
 #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,
     uint32_t *eax, void *arg)
 {
-	static uint16_t pm1_control;
 
 	if (bytes != 2)
 		return (-1);
@@ -121,9 +230,11 @@ pm1_control_handler(struct vmctx *ctx, i
 	else {
 		/*
 		 * 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
@@ -137,3 +248,41 @@ pm1_control_handler(struct vmctx *ctx, i
 	return (0);
 }
 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);

Modified: head/usr.sbin/bhyve/pmtmr.c
==============================================================================
--- head/usr.sbin/bhyve/pmtmr.c	Sat Dec 28 03:21:15 2013	(r259997)
+++ head/usr.sbin/bhyve/pmtmr.c	Sat Dec 28 04:01:05 2013	(r259998)
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
 #include <assert.h>
 #include <pthread.h>
 
+#include "acpi.h"
 #include "inout.h"
 
 /*
@@ -49,8 +50,6 @@ __FBSDID("$FreeBSD$");
  * This implementation will be 32-bits
  */
 
-#define	IO_PMTMR	0x408	/* 4-byte i/o port for the timer */
-
 #define PMTMR_FREQ	3579545  /* 3.579545MHz */
 
 static pthread_mutex_t pmtmr_mtx;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201312280401.rBS415Uu081102>