direct-io.hg

view xen/arch/x86/vmx_intercept.c @ 5558:1a568bb6c3bb

bitkeeper revision 1.1740 (42ba88cewc_4MMbiCLInZ8gEcgqwhQ)

[PATCH] vmx-pit-reset2.patch

A VMX guest can reprogram the same PIT channel twice. We should not create
two ac_timers to handle this case.

Signed-off-by: Edwin Zhai <edwin.zhai@intel.com>
Signed-off-by: Arun Sharma <arun.sharma@intel.com>
author arun.sharma@intel.com[kaf24]
date Thu Jun 23 10:02:54 2005 +0000 (2005-06-23)
parents 8651a99cdc09
children 6daf7357a9df
line source
1 /*
2 * vmx_intercept.c: Handle performance critical I/O packets in hypervisor space
3 * Copyright (c) 2004, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307 USA.
17 *
18 */
20 #include <xen/config.h>
21 #include <xen/types.h>
22 #include <asm/vmx.h>
23 #include <asm/vmx_platform.h>
24 #include <asm/vmx_virpit.h>
25 #include <asm/vmx_intercept.h>
26 #include <public/io/ioreq.h>
28 #include <xen/lib.h>
29 #include <xen/sched.h>
30 #include <asm/current.h>
32 #ifdef CONFIG_VMX
34 /* for intercepting io request after vm_exit, return value: 0--not handle; 1--handled */
35 int vmx_io_intercept(ioreq_t *p)
36 {
37 struct vcpu *d = current;
38 struct vmx_handler_t *handler = &(d->arch.arch_vmx.vmx_platform.vmx_handler);
39 int i;
40 unsigned long addr, offset;
41 for (i = 0; i < handler->num_slot; i++) {
42 addr = handler->hdl_list[i].addr;
43 offset = handler->hdl_list[i].offset;
44 if (p->addr >= addr &&
45 p->addr < addr + offset)
46 return handler->hdl_list[i].action(p);
47 }
48 return 0;
49 }
51 int register_io_handler(unsigned long addr, unsigned long offset, intercept_action_t action)
52 {
53 struct vcpu *d = current;
54 struct vmx_handler_t *handler = &(d->arch.arch_vmx.vmx_platform.vmx_handler);
55 int num = handler->num_slot;
57 if (num >= MAX_IO_HANDLER) {
58 printk("no extra space, register io interceptor failed!\n");
59 domain_crash_synchronous();
60 }
62 handler->hdl_list[num].addr = addr;
63 handler->hdl_list[num].offset = offset;
64 handler->hdl_list[num].action = action;
65 handler->num_slot++;
66 return 1;
68 }
70 static void pit_cal_count(struct vmx_virpit_t *vpit)
71 {
72 unsigned int usec_delta = (unsigned int)((NOW() - vpit->inject_point) / 1000);
73 if (usec_delta > vpit->period * 1000)
74 VMX_DBG_LOG(DBG_LEVEL_1, "VMX_PIT:long time has passed from last injection!");
75 vpit->count = vpit->init_val - ((usec_delta * PIT_FREQ / 1000000) % vpit->init_val );
76 }
78 static void pit_latch_io(struct vmx_virpit_t *vpit)
79 {
80 pit_cal_count(vpit);
82 switch(vpit->read_state) {
83 case MSByte:
84 vpit->count_MSB_latched=1;
85 break;
86 case LSByte:
87 vpit->count_LSB_latched=1;
88 break;
89 case LSByte_multiple:
90 vpit->count_LSB_latched=1;
91 vpit->count_MSB_latched=1;
92 break;
93 case MSByte_multiple:
94 VMX_DBG_LOG(DBG_LEVEL_1, "VMX_PIT:latch PIT counter before MSB_multiple!");
95 vpit->read_state=LSByte_multiple;
96 vpit->count_LSB_latched=1;
97 vpit->count_MSB_latched=1;
98 break;
99 default:
100 BUG();
101 }
102 }
104 static int pit_read_io(struct vmx_virpit_t *vpit)
105 {
106 if(vpit->count_LSB_latched) {
107 /* Read Least Significant Byte */
108 if(vpit->read_state==LSByte_multiple) {
109 vpit->read_state=MSByte_multiple;
110 }
111 vpit->count_LSB_latched=0;
112 return (vpit->count & 0xFF);
113 } else if(vpit->count_MSB_latched) {
114 /* Read Most Significant Byte */
115 if(vpit->read_state==MSByte_multiple) {
116 vpit->read_state=LSByte_multiple;
117 }
118 vpit->count_MSB_latched=0;
119 return ((vpit->count>>8) & 0xFF);
120 } else {
121 /* Unlatched Count Read */
122 VMX_DBG_LOG(DBG_LEVEL_1, "VMX_PIT: unlatched read");
123 pit_cal_count(vpit);
124 if(!(vpit->read_state & 0x1)) {
125 /* Read Least Significant Byte */
126 if(vpit->read_state==LSByte_multiple) {
127 vpit->read_state=MSByte_multiple;
128 }
129 return (vpit->count & 0xFF);
130 } else {
131 /* Read Most Significant Byte */
132 if(vpit->read_state==MSByte_multiple) {
133 vpit->read_state=LSByte_multiple;
134 }
135 return ((vpit->count>>8) & 0xFF);
136 }
137 }
138 }
140 /* vmx_io_assist light-weight version, specific to PIT DM */
141 static void resume_pit_io(ioreq_t *p)
142 {
143 struct cpu_user_regs *regs = guest_cpu_user_regs();
144 unsigned long old_eax = regs->eax;
145 p->state = STATE_INVALID;
147 switch(p->size) {
148 case 1:
149 regs->eax = (old_eax & 0xffffff00) | (p->u.data & 0xff);
150 break;
151 case 2:
152 regs->eax = (old_eax & 0xffff0000) | (p->u.data & 0xffff);
153 break;
154 case 4:
155 regs->eax = (p->u.data & 0xffffffff);
156 break;
157 default:
158 BUG();
159 }
160 }
162 /* the intercept action for PIT DM retval:0--not handled; 1--handled */
163 int intercept_pit_io(ioreq_t *p)
164 {
165 struct vcpu *d = current;
166 struct vmx_virpit_t *vpit = &(d->arch.arch_vmx.vmx_platform.vmx_pit);
168 if (p->size != 1 ||
169 p->pdata_valid ||
170 p->port_mm)
171 return 0;
173 if (p->addr == 0x43 &&
174 p->dir == 0 && /* write */
175 ((p->u.data >> 4) & 0x3) == 0 && /* latch command */
176 ((p->u.data >> 6) & 0x3) == (vpit->channel)) {/* right channel */
177 pit_latch_io(vpit);
178 return 1;
179 }
181 if (p->addr == (0x40 + vpit->channel) &&
182 p->dir == 1) { /* read */
183 p->u.data = pit_read_io(vpit);
184 resume_pit_io(p);
185 return 1;
186 }
188 return 0;
189 }
191 /* hooks function for the PIT initialization response iopacket */
192 static void pit_timer_fn(void *data)
193 {
194 struct vmx_virpit_t *vpit = data;
196 /* Set the pending intr bit, and send evtchn notification to myself. */
197 if (test_and_set_bit(vpit->vector, vpit->intr_bitmap))
198 vpit->pending_intr_nr++; /* already set, then count the pending intr */
200 set_ac_timer(&vpit->pit_timer, NOW() + MILLISECS(vpit->period));
201 }
204 /* Only some PIT operations such as load init counter need a hypervisor hook.
205 * leave all other operations in user space DM
206 */
207 void vmx_hooks_assist(struct vcpu *d)
208 {
209 vcpu_iodata_t *vio = (vcpu_iodata_t *) d->arch.arch_vmx.vmx_platform.shared_page_va;
210 ioreq_t *p = &vio->vp_ioreq;
211 unsigned long *intr = &(vio->vp_intr[0]);
212 struct vmx_virpit_t *vpit = &(d->arch.arch_vmx.vmx_platform.vmx_pit);
213 int rw_mode;
215 /* load init count*/
216 if (p->state == STATE_IORESP_HOOK) {
217 /* set up actimer, handle re-init */
218 if ( active_ac_timer(&(vpit->pit_timer)) ) {
219 VMX_DBG_LOG(DBG_LEVEL_1, "VMX_PIT: guest reset PIT with channel %lx!\n", (unsigned long) ((p->u.data >> 24) & 0x3) );
220 rem_ac_timer(&(vpit->pit_timer));
221 }
222 else
223 init_ac_timer(&vpit->pit_timer, pit_timer_fn, vpit, 0);
225 /* init count for this channel */
226 vpit->init_val = (p->u.data & 0xFFFF) ;
227 /* frequency(ms) of pit */
228 vpit->period = DIV_ROUND(((vpit->init_val) * 1000), PIT_FREQ);
229 if (vpit->period < 1) {
230 printk("VMX_PIT: guest programmed too small an init_val: %x\n",
231 vpit->init_val);
232 vpit->period = 1;
233 }
234 vpit->vector = ((p->u.data >> 16) & 0xFF);
235 vpit->channel = ((p->u.data >> 24) & 0x3);
236 vpit->first_injected = 0;
238 vpit->count_LSB_latched = 0;
239 vpit->count_MSB_latched = 0;
241 rw_mode = ((p->u.data >> 26) & 0x3);
242 switch(rw_mode) {
243 case 0x1:
244 vpit->read_state=LSByte;
245 break;
246 case 0x2:
247 vpit->read_state=MSByte;
248 break;
249 case 0x3:
250 vpit->read_state=LSByte_multiple;
251 break;
252 default:
253 printk("VMX_PIT:wrong PIT rw_mode!\n");
254 break;
255 }
257 vpit->intr_bitmap = intr;
259 set_ac_timer(&vpit->pit_timer, NOW() + MILLISECS(vpit->period));
261 /*restore the state*/
262 p->state = STATE_IORESP_READY;
264 /* register handler to intercept the PIT io when vm_exit */
265 register_io_handler(0x40, 4, intercept_pit_io);
266 }
268 }
270 #endif /* CONFIG_VMX */