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