pm.c revision 259826
1/*-
2 * Copyright (c) 2013 Advanced Computing Technologies 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__FBSDID("$FreeBSD: head/usr.sbin/bhyve/pm.c 259826 2013-12-24 16:14:19Z jhb $");
30
31#include <sys/types.h>
32
33#include "inout.h"
34
35#define	PM1A_EVT_ADDR	0x400
36#define	PM1A_CNT_ADDR	0x404
37
38/*
39 * Reset Control register at I/O port 0xcf9.  Bit 2 forces a system
40 * reset when it transitions from 0 to 1.  Bit 1 selects the type of
41 * reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
42 * reset.
43 */
44static int
45reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
46    uint32_t *eax, void *arg)
47{
48	static uint8_t reset_control;
49
50	if (bytes != 1)
51		return (-1);
52	if (in)
53		*eax = reset_control;
54	else {
55		reset_control = *eax;
56
57		/* Treat hard and soft resets the same. */
58		if (reset_control & 0x4)
59			return (INOUT_RESET);
60	}
61	return (0);
62}
63INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
64
65/*
66 * Power Management 1 Event Registers
67 *
68 * bhyve doesn't support any power management events currently, so the
69 * status register always returns zero.  The enable register preserves
70 * its value but has no effect.
71 */
72static int
73pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
74    uint32_t *eax, void *arg)
75{
76
77	if (bytes != 2)
78		return (-1);
79	if (in)
80		*eax = 0;
81	return (0);
82}
83
84static int
85pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
86    uint32_t *eax, void *arg)
87{
88	static uint16_t pm1_enable;
89
90	if (bytes != 2)
91		return (-1);
92	if (in)
93		*eax = pm1_enable;
94	else
95		pm1_enable = *eax;
96	return (0);
97}
98INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
99INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
100
101/*
102 * Power Management 1 Control Register
103 *
104 * This is mostly unimplemented except that we wish to handle writes that
105 * set SPL_EN to handle S5 (soft power off).
106 */
107#define	PM1_SLP_TYP	0x1c00
108#define	PM1_SLP_EN	0x2000
109#define	PM1_ALWAYS_ZERO	0xc003
110
111static int
112pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
113    uint32_t *eax, void *arg)
114{
115	static uint16_t pm1_control;
116
117	if (bytes != 2)
118		return (-1);
119	if (in)
120		*eax = pm1_control;
121	else {
122		/*
123		 * Various bits are write-only or reserved, so force them
124		 * to zero in pm1_control.
125		 */
126		pm1_control = *eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO);
127
128		/*
129		 * If SLP_EN is set, check for S5.  Bhyve's _S5_ method
130		 * says that '5' should be stored in SLP_TYP for S5.
131		 */
132		if (*eax & PM1_SLP_EN) {
133			if ((pm1_control & PM1_SLP_TYP) >> 10 == 5)
134				return (INOUT_POWEROFF);
135		}
136	}
137	return (0);
138}
139INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
140