vmm_lapic.c revision 268953
1/*-
2 * Copyright (c) 2011 NetApp, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/10/sys/amd64/vmm/vmm_lapic.c 268953 2014-07-21 19:08:02Z jhb $
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/vmm_lapic.c 268953 2014-07-21 19:08:02Z jhb $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/smp.h>
35
36#include <x86/specialreg.h>
37#include <x86/apicreg.h>
38
39#include <machine/vmm.h>
40#include "vmm_ipi.h"
41#include "vmm_ktr.h"
42#include "vmm_lapic.h"
43#include "vlapic.h"
44
45/*
46 * Some MSI message definitions
47 */
48#define	MSI_X86_ADDR_MASK	0xfff00000
49#define	MSI_X86_ADDR_BASE	0xfee00000
50#define	MSI_X86_ADDR_RH		0x00000008	/* Redirection Hint */
51#define	MSI_X86_ADDR_LOG	0x00000004	/* Destination Mode */
52
53int
54lapic_set_intr(struct vm *vm, int cpu, int vector, bool level)
55{
56	struct vlapic *vlapic;
57
58	if (cpu < 0 || cpu >= VM_MAXCPU)
59		return (EINVAL);
60
61	if (vector < 32 || vector > 255)
62		return (EINVAL);
63
64	vlapic = vm_lapic(vm, cpu);
65	if (vlapic_set_intr_ready(vlapic, vector, level))
66		vcpu_notify_event(vm, cpu, true);
67	return (0);
68}
69
70int
71lapic_set_local_intr(struct vm *vm, int cpu, int vector)
72{
73	struct vlapic *vlapic;
74	cpuset_t dmask;
75	int error;
76
77	if (cpu < -1 || cpu >= VM_MAXCPU)
78		return (EINVAL);
79
80	if (cpu == -1)
81		dmask = vm_active_cpus(vm);
82	else
83		CPU_SETOF(cpu, &dmask);
84	error = 0;
85	while ((cpu = CPU_FFS(&dmask)) != 0) {
86		cpu--;
87		CPU_CLR(cpu, &dmask);
88		vlapic = vm_lapic(vm, cpu);
89		error = vlapic_trigger_lvt(vlapic, vector);
90		if (error)
91			break;
92	}
93
94	return (error);
95}
96
97int
98lapic_intr_msi(struct vm *vm, uint64_t addr, uint64_t msg)
99{
100	int delmode, vec;
101	uint32_t dest;
102	bool phys;
103
104	VM_CTR2(vm, "lapic MSI addr: %#lx msg: %#lx", addr, msg);
105
106	if ((addr & MSI_X86_ADDR_MASK) != MSI_X86_ADDR_BASE) {
107		VM_CTR1(vm, "lapic MSI invalid addr %#lx", addr);
108		return (-1);
109	}
110
111	/*
112	 * Extract the x86-specific fields from the MSI addr/msg
113	 * params according to the Intel Arch spec, Vol3 Ch 10.
114	 *
115	 * The PCI specification does not support level triggered
116	 * MSI/MSI-X so ignore trigger level in 'msg'.
117	 *
118	 * The 'dest' is interpreted as a logical APIC ID if both
119	 * the Redirection Hint and Destination Mode are '1' and
120	 * physical otherwise.
121	 */
122	dest = (addr >> 12) & 0xff;
123	phys = ((addr & (MSI_X86_ADDR_RH | MSI_X86_ADDR_LOG)) !=
124	    (MSI_X86_ADDR_RH | MSI_X86_ADDR_LOG));
125	delmode = msg & APIC_DELMODE_MASK;
126	vec = msg & 0xff;
127
128	VM_CTR3(vm, "lapic MSI %s dest %#x, vec %d",
129	    phys ? "physical" : "logical", dest, vec);
130
131	vlapic_deliver_intr(vm, LAPIC_TRIG_EDGE, dest, phys, delmode, vec);
132	return (0);
133}
134
135static boolean_t
136x2apic_msr(u_int msr)
137{
138	if (msr >= 0x800 && msr <= 0xBFF)
139		return (TRUE);
140	else
141		return (FALSE);
142}
143
144static u_int
145x2apic_msr_to_regoff(u_int msr)
146{
147
148	return ((msr - 0x800) << 4);
149}
150
151boolean_t
152lapic_msr(u_int msr)
153{
154
155	if (x2apic_msr(msr) || (msr == MSR_APICBASE))
156		return (TRUE);
157	else
158		return (FALSE);
159}
160
161int
162lapic_rdmsr(struct vm *vm, int cpu, u_int msr, uint64_t *rval, bool *retu)
163{
164	int error;
165	u_int offset;
166	struct vlapic *vlapic;
167
168	vlapic = vm_lapic(vm, cpu);
169
170	if (msr == MSR_APICBASE) {
171		*rval = vlapic_get_apicbase(vlapic);
172		error = 0;
173	} else {
174		offset = x2apic_msr_to_regoff(msr);
175		error = vlapic_read(vlapic, 0, offset, rval, retu);
176	}
177
178	return (error);
179}
180
181int
182lapic_wrmsr(struct vm *vm, int cpu, u_int msr, uint64_t val, bool *retu)
183{
184	int error;
185	u_int offset;
186	struct vlapic *vlapic;
187
188	vlapic = vm_lapic(vm, cpu);
189
190	if (msr == MSR_APICBASE) {
191		error = vlapic_set_apicbase(vlapic, val);
192	} else {
193		offset = x2apic_msr_to_regoff(msr);
194		error = vlapic_write(vlapic, 0, offset, val, retu);
195	}
196
197	return (error);
198}
199
200int
201lapic_mmio_write(void *vm, int cpu, uint64_t gpa, uint64_t wval, int size,
202		 void *arg)
203{
204	int error;
205	uint64_t off;
206	struct vlapic *vlapic;
207
208	off = gpa - DEFAULT_APIC_BASE;
209
210	/*
211	 * Memory mapped local apic accesses must be 4 bytes wide and
212	 * aligned on a 16-byte boundary.
213	 */
214	if (size != 4 || off & 0xf)
215		return (EINVAL);
216
217	vlapic = vm_lapic(vm, cpu);
218	error = vlapic_write(vlapic, 1, off, wval, arg);
219	return (error);
220}
221
222int
223lapic_mmio_read(void *vm, int cpu, uint64_t gpa, uint64_t *rval, int size,
224		void *arg)
225{
226	int error;
227	uint64_t off;
228	struct vlapic *vlapic;
229
230	off = gpa - DEFAULT_APIC_BASE;
231
232	/*
233	 * Memory mapped local apic accesses should be aligned on a
234	 * 16-byte boundary.  They are also suggested to be 4 bytes
235	 * wide, alas not all OSes follow suggestions.
236	 */
237	off &= ~3;
238	if (off & 0xf)
239		return (EINVAL);
240
241	vlapic = vm_lapic(vm, cpu);
242	error = vlapic_read(vlapic, 1, off, rval, arg);
243	return (error);
244}
245