Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/amd64/vmm/vmm_ioport.c
107937 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2014 Tycho Nightingale <[email protected]>
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 THE AUTHOR ``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 THE AUTHOR 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
32
#include <machine/vmm.h>
33
#include <machine/vmm_instruction_emul.h>
34
35
#include <dev/vmm/vmm_ktr.h>
36
#include <dev/vmm/vmm_vm.h>
37
38
#include "vatpic.h"
39
#include "vatpit.h"
40
#include "vpmtmr.h"
41
#include "vrtc.h"
42
#include "vmm_ioport.h"
43
44
#define MAX_IOPORTS 1280
45
46
ioport_handler_func_t ioport_handler[MAX_IOPORTS] = {
47
[TIMER_MODE] = vatpit_handler,
48
[TIMER_CNTR0] = vatpit_handler,
49
[TIMER_CNTR1] = vatpit_handler,
50
[TIMER_CNTR2] = vatpit_handler,
51
[NMISC_PORT] = vatpit_nmisc_handler,
52
[IO_ICU1] = vatpic_master_handler,
53
[IO_ICU1 + ICU_IMR_OFFSET] = vatpic_master_handler,
54
[IO_ICU2] = vatpic_slave_handler,
55
[IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler,
56
[IO_ELCR1] = vatpic_elc_handler,
57
[IO_ELCR2] = vatpic_elc_handler,
58
[IO_PMTMR] = vpmtmr_handler,
59
[IO_RTC] = vrtc_addr_handler,
60
[IO_RTC + 1] = vrtc_data_handler,
61
};
62
63
#ifdef KTR
64
static const char *
65
inout_instruction(struct vm_exit *vmexit)
66
{
67
int index;
68
69
static const char *iodesc[] = {
70
"outb", "outw", "outl",
71
"inb", "inw", "inl",
72
"outsb", "outsw", "outsd",
73
"insb", "insw", "insd",
74
};
75
76
switch (vmexit->u.inout.bytes) {
77
case 1:
78
index = 0;
79
break;
80
case 2:
81
index = 1;
82
break;
83
default:
84
index = 2;
85
break;
86
}
87
88
if (vmexit->u.inout.in)
89
index += 3;
90
91
if (vmexit->u.inout.string)
92
index += 6;
93
94
KASSERT(index < nitems(iodesc), ("%s: invalid index %d",
95
__func__, index));
96
97
return (iodesc[index]);
98
}
99
#endif /* KTR */
100
101
static int
102
emulate_inout_port(struct vcpu *vcpu, struct vm_exit *vmexit, bool *retu)
103
{
104
ioport_handler_func_t handler;
105
uint32_t mask, val = 0;
106
int error;
107
108
/*
109
* If there is no handler for the I/O port then punt to userspace.
110
*/
111
if (vmexit->u.inout.port >= MAX_IOPORTS ||
112
(handler = ioport_handler[vmexit->u.inout.port]) == NULL) {
113
*retu = true;
114
return (0);
115
}
116
117
mask = vie_size2mask(vmexit->u.inout.bytes);
118
119
if (!vmexit->u.inout.in) {
120
val = vmexit->u.inout.eax & mask;
121
}
122
123
error = (*handler)(vcpu_vm(vcpu), vmexit->u.inout.in,
124
vmexit->u.inout.port, vmexit->u.inout.bytes, &val);
125
if (error) {
126
/*
127
* The value returned by this function is also the return value
128
* of vm_run(). This needs to be a positive number otherwise it
129
* can be interpreted as a "pseudo-error" like ERESTART.
130
*
131
* Enforce this by mapping all errors to EIO.
132
*/
133
return (EIO);
134
}
135
136
if (vmexit->u.inout.in) {
137
vmexit->u.inout.eax &= ~mask;
138
vmexit->u.inout.eax |= val & mask;
139
error = vm_set_register(vcpu, VM_REG_GUEST_RAX,
140
vmexit->u.inout.eax);
141
KASSERT(error == 0, ("emulate_ioport: error %d setting guest "
142
"rax register", error));
143
}
144
*retu = false;
145
return (0);
146
}
147
148
static int
149
decode_segment(struct vcpu *vcpu, enum vm_reg_name *segment)
150
{
151
struct vm_guest_paging *paging;
152
struct vie vie;
153
struct vm_exit *vme;
154
int err;
155
int fault;
156
157
vme = vm_exitinfo(vcpu);
158
paging = &vme->u.inout_str.paging;
159
160
vie_init(&vie, NULL, 0);
161
err = vmm_fetch_instruction(vcpu, paging,
162
vme->rip + vme->u.inout_str.cs_base, VIE_INST_SIZE, &vie, &fault);
163
if (err || fault)
164
return (err);
165
166
err = vmm_decode_instruction(vcpu, VIE_INVALID_GLA, paging->cpu_mode,
167
vme->u.inout_str.cs_d, &vie);
168
169
if (err || vie.op.op_type != VIE_OP_TYPE_OUTS)
170
return (EINVAL);
171
if (vie.segment_override)
172
*segment = vie.segment_register;
173
else
174
*segment = VM_REG_GUEST_DS;
175
176
return (0);
177
}
178
179
static int
180
emulate_inout_str(struct vcpu *vcpu, struct vm_exit *vmexit, bool *retu)
181
{
182
int err;
183
184
*retu = true;
185
if (vmexit->u.inout_str.seg_name == VM_REG_LAST) {
186
err = decode_segment(vcpu, &vmexit->u.inout_str.seg_name);
187
if (err)
188
return (err);
189
return (vm_get_seg_desc(vcpu, vmexit->u.inout_str.seg_name,
190
&vmexit->u.inout_str.seg_desc));
191
}
192
return (0); /* Return to userspace to finish emulation */
193
}
194
195
int
196
vm_handle_inout(struct vcpu *vcpu, struct vm_exit *vmexit, bool *retu)
197
{
198
int bytes __diagused, error;
199
200
bytes = vmexit->u.inout.bytes;
201
KASSERT(bytes == 1 || bytes == 2 || bytes == 4,
202
("vm_handle_inout: invalid operand size %d", bytes));
203
204
if (vmexit->u.inout.string)
205
error = emulate_inout_str(vcpu, vmexit, retu);
206
else
207
error = emulate_inout_port(vcpu, vmexit, retu);
208
209
VCPU_CTR4(vcpu_vm(vcpu), vcpu_vcpuid(vcpu), "%s%s 0x%04x: %s",
210
vmexit->u.inout.rep ? "rep " : "",
211
inout_instruction(vmexit),
212
vmexit->u.inout.port,
213
error ? "error" : (*retu ? "userspace" : "handled"));
214
215
return (error);
216
}
217
218