Support soft power-off via the ACPI S5 state for bhyve guests.
- Implement the PM1_EVT and PM1_CTL registers required by ACPI. The PM1_EVT register is mostly a dummy as bhyve doesn't support any of the hardware-initiated events. The only bit of PM1_CNT that is implemented are the sleep request bits (SPL_EN and SLP_TYP) which request a graceful power off for S5. In particular, for S5, bhyve exits with a non-zero value which terminates the loop in vmrun.sh. - Emulate the Reset Control register at I/O port 0xcf9 and advertise it as the reset register via ACPI. - Advertise an _S5 package. - Extend the in/out interface to allow an in/out handler to request that the hypervisor trigger a reset or power-off. - While here, note that all vCPUs in a guest support C1 ("hlt"). Reviewed by: neel (earlier version)
This commit is contained in:
parent
3c6aaa556d
commit
6450da0774
@ -10,7 +10,7 @@ MAN= bhyve.8
|
||||
SRCS= acpi.c atpic.c bhyverun.c block_if.c consport.c dbgport.c elcr.c
|
||||
SRCS+= inout.c legacy_irq.c mem.c mevent.c mptbl.c pci_ahci.c
|
||||
SRCS+= pci_emul.c pci_hostbridge.c pci_lpc.c pci_passthru.c pci_virtio_block.c
|
||||
SRCS+= pci_virtio_net.c pci_uart.c pit_8254.c pmtmr.c post.c rtc.c
|
||||
SRCS+= pci_virtio_net.c pci_uart.c pit_8254.c pm.c pmtmr.c post.c rtc.c
|
||||
SRCS+= uart_emul.c virtio.c xmsr.c spinup_ap.c
|
||||
|
||||
.PATH: ${.CURDIR}/../../sys/amd64/vmm
|
||||
|
@ -85,6 +85,8 @@ __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;
|
||||
@ -342,9 +344,11 @@ basl_fwrite_fadt(FILE *fp)
|
||||
EFPRINTF(fp, "[0001]\t\tACPI Disable Value : 00\n");
|
||||
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 : 00000000\n");
|
||||
EFPRINTF(fp, "[0004]\t\tPM1A Event Block Address : %08X\n",
|
||||
BHYVE_PM1A_EVT_ADDR);
|
||||
EFPRINTF(fp, "[0004]\t\tPM1B Event Block Address : 00000000\n");
|
||||
EFPRINTF(fp, "[0004]\t\tPM1A Control Block Address : 00000000\n");
|
||||
EFPRINTF(fp, "[0004]\t\tPM1A Control Block Address : %08X\n",
|
||||
BHYVE_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",
|
||||
@ -379,7 +383,7 @@ basl_fwrite_fadt(FILE *fp)
|
||||
EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000000\n");
|
||||
EFPRINTF(fp, "\t\t\tWBINVD instruction is operational (V1) : 1\n");
|
||||
EFPRINTF(fp, "\t\t\tWBINVD flushes all caches (V1) : 0\n");
|
||||
EFPRINTF(fp, "\t\t\tAll CPUs support C1 (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 Sleep Button (V1) : 1\n");
|
||||
@ -387,7 +391,7 @@ basl_fwrite_fadt(FILE *fp)
|
||||
EFPRINTF(fp, "\t\t\tRTC can wake system from S4 (V1) : 0\n");
|
||||
EFPRINTF(fp, "\t\t\t32-bit PM Timer (V1) : 1\n");
|
||||
EFPRINTF(fp, "\t\t\tDocking Supported (V1) : 0\n");
|
||||
EFPRINTF(fp, "\t\t\tReset Register Supported (V2) : 0\n");
|
||||
EFPRINTF(fp, "\t\t\tReset Register Supported (V2) : 1\n");
|
||||
EFPRINTF(fp, "\t\t\tSealed Case (V3) : 0\n");
|
||||
EFPRINTF(fp, "\t\t\tHeadless - No Video (V3) : 1\n");
|
||||
EFPRINTF(fp, "\t\t\tUse native instr after SLP_TYPx (V3) : 0\n");
|
||||
@ -407,10 +411,10 @@ basl_fwrite_fadt(FILE *fp)
|
||||
EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
|
||||
EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
|
||||
EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 01 [Byte Access:8]\n");
|
||||
EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000001\n");
|
||||
EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000CF9\n");
|
||||
EFPRINTF(fp, "\n");
|
||||
|
||||
EFPRINTF(fp, "[0001]\t\tValue to cause reset : 00\n");
|
||||
EFPRINTF(fp, "[0001]\t\tValue to cause reset : 06\n");
|
||||
EFPRINTF(fp, "[0003]\t\tReserved : 000000\n");
|
||||
EFPRINTF(fp, "[0008]\t\tFACS Address : 00000000%08X\n",
|
||||
basl_acpi_base + FACS_OFFSET);
|
||||
@ -422,7 +426,8 @@ basl_fwrite_fadt(FILE *fp)
|
||||
EFPRINTF(fp, "[0001]\t\tBit Width : 20\n");
|
||||
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 : 0000000000000001\n");
|
||||
EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
|
||||
BHYVE_PM1A_EVT_ADDR);
|
||||
EFPRINTF(fp, "\n");
|
||||
|
||||
EFPRINTF(fp,
|
||||
@ -441,7 +446,8 @@ basl_fwrite_fadt(FILE *fp)
|
||||
EFPRINTF(fp, "[0001]\t\tBit Width : 10\n");
|
||||
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 : 0000000000000001\n");
|
||||
EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
|
||||
BHYVE_PM1A_CNT_ADDR);
|
||||
EFPRINTF(fp, "\n");
|
||||
|
||||
EFPRINTF(fp,
|
||||
@ -613,6 +619,11 @@ basl_fwrite_dsdt(FILE *fp)
|
||||
EFPRINTF(fp, "DefinitionBlock (\"bhyve_dsdt.aml\", \"DSDT\", 2,"
|
||||
"\"BHYVE \", \"BVDSDT \", 0x00000001)\n");
|
||||
EFPRINTF(fp, "{\n");
|
||||
EFPRINTF(fp, " Name (_S5, Package (0x02)\n");
|
||||
EFPRINTF(fp, " {\n");
|
||||
EFPRINTF(fp, " 0x05,\n");
|
||||
EFPRINTF(fp, " Zero,\n");
|
||||
EFPRINTF(fp, " })\n");
|
||||
EFPRINTF(fp, " Scope (_SB)\n");
|
||||
EFPRINTF(fp, " {\n");
|
||||
EFPRINTF(fp, " Device (PCI0)\n");
|
||||
|
@ -72,6 +72,7 @@ __FBSDID("$FreeBSD$");
|
||||
#define VMEXIT_RESTART 2 /* restart current instruction */
|
||||
#define VMEXIT_ABORT 3 /* abort the vm run loop */
|
||||
#define VMEXIT_RESET 4 /* guest machine has reset */
|
||||
#define VMEXIT_POWEROFF 5 /* guest machine has powered off */
|
||||
|
||||
#define MB (1024UL * 1024)
|
||||
#define GB (1024UL * MB)
|
||||
@ -296,12 +297,17 @@ vmexit_inout(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu)
|
||||
return (vmexit_handle_notify(ctx, vme, pvcpu, eax));
|
||||
|
||||
error = emulate_inout(ctx, vcpu, in, port, bytes, &eax, strictio);
|
||||
if (error == 0 && in)
|
||||
if (error == INOUT_OK && in)
|
||||
error = vm_set_register(ctx, vcpu, VM_REG_GUEST_RAX, eax);
|
||||
|
||||
if (error == 0)
|
||||
switch (error) {
|
||||
case INOUT_OK:
|
||||
return (VMEXIT_CONTINUE);
|
||||
else {
|
||||
case INOUT_RESET:
|
||||
return (VMEXIT_RESET);
|
||||
case INOUT_POWEROFF:
|
||||
return (VMEXIT_POWEROFF);
|
||||
default:
|
||||
fprintf(stderr, "Unhandled %s%c 0x%04x\n",
|
||||
in ? "in" : "out",
|
||||
bytes == 1 ? 'b' : (bytes == 2 ? 'w' : 'l'), port);
|
||||
|
@ -33,6 +33,12 @@
|
||||
|
||||
struct vmctx;
|
||||
|
||||
/* Handler return values. */
|
||||
#define INOUT_ERROR -1
|
||||
#define INOUT_OK 0
|
||||
#define INOUT_RESET 1
|
||||
#define INOUT_POWEROFF 2
|
||||
|
||||
typedef int (*inout_func_t)(struct vmctx *ctx, int vcpu, int in, int port,
|
||||
int bytes, uint32_t *eax, void *arg);
|
||||
|
||||
|
139
usr.sbin/bhyve/pm.c
Normal file
139
usr.sbin/bhyve/pm.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Advanced Computing Technologies LLC
|
||||
* Written by: John H. Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "inout.h"
|
||||
|
||||
#define PM1A_EVT_ADDR 0x400
|
||||
#define PM1A_CNT_ADDR 0x404
|
||||
|
||||
/*
|
||||
* Reset Control register at I/O port 0xcf9. Bit 2 forces a system
|
||||
* reset when it transitions from 0 to 1. Bit 1 selects the type of
|
||||
* reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
|
||||
* reset.
|
||||
*/
|
||||
static int
|
||||
reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||
uint32_t *eax, void *arg)
|
||||
{
|
||||
static uint8_t reset_control;
|
||||
|
||||
if (bytes != 1)
|
||||
return (-1);
|
||||
if (in)
|
||||
*eax = reset_control;
|
||||
else {
|
||||
reset_control = *eax;
|
||||
|
||||
/* Treat hard and soft resets the same. */
|
||||
if (reset_control & 0x4)
|
||||
return (INOUT_RESET);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static int
|
||||
pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||
uint32_t *eax, void *arg)
|
||||
{
|
||||
|
||||
if (bytes != 2)
|
||||
return (-1);
|
||||
if (in)
|
||||
*eax = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
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);
|
||||
if (in)
|
||||
*eax = pm1_enable;
|
||||
else
|
||||
pm1_enable = *eax;
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
#define PM1_SLP_TYP 0x1c00
|
||||
#define PM1_SLP_EN 0x2000
|
||||
#define PM1_ALWAYS_ZERO 0xc003
|
||||
|
||||
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);
|
||||
if (in)
|
||||
*eax = pm1_control;
|
||||
else {
|
||||
/*
|
||||
* Various bits are write-only or reserved, so force them
|
||||
* to zero in pm1_control.
|
||||
*/
|
||||
pm1_control = *eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO);
|
||||
|
||||
/*
|
||||
* If SLP_EN is set, check for S5. Bhyve's _S5_ method
|
||||
* says that '5' should be stored in SLP_TYP for S5.
|
||||
*/
|
||||
if (*eax & PM1_SLP_EN) {
|
||||
if ((pm1_control & PM1_SLP_TYP) >> 10 == 5)
|
||||
return (INOUT_POWEROFF);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
|
Loading…
Reference in New Issue
Block a user