xref: /freebsd/sys/amd64/vmm/vmm_lapic.c (revision 40a8ac8f62b535d30349faf28cf47106b7041b83)
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$
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
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 
53 int
54 lapic_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 
70 int
71 lapic_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 
97 int
98 lapic_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 
135 static boolean_t
136 x2apic_msr(u_int msr)
137 {
138 	if (msr >= 0x800 && msr <= 0xBFF)
139 		return (TRUE);
140 	else
141 		return (FALSE);
142 }
143 
144 static u_int
145 x2apic_msr_to_regoff(u_int msr)
146 {
147 
148 	return ((msr - 0x800) << 4);
149 }
150 
151 boolean_t
152 lapic_msr(u_int msr)
153 {
154 
155 	if (x2apic_msr(msr) || (msr == MSR_APICBASE))
156 		return (TRUE);
157 	else
158 		return (FALSE);
159 }
160 
161 int
162 lapic_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 
181 int
182 lapic_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 
200 int
201 lapic_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 
222 int
223 lapic_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