direct-io.hg

view xen/arch/x86/vmx_intercept.c @ 5647:6f462a11a08e

Register the portio handler only once.
Signed-off-by: Arun Sharma <arun.sharma@intel.com>
author kaf24@firebug.cl.cam.ac.uk
date Fri Jul 01 21:25:45 2005 +0000 (2005-07-01)
parents 4669354bba9a
children 43564304cf94 b53a65034532
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 /* Check if the request is handled inside xen
35 return value: 0 --not handled; 1 --handled */
36 int vmx_io_intercept(ioreq_t *p, int type)
37 {
38 struct vcpu *d = current;
39 struct vmx_handler_t *handler = &(d->domain->arch.vmx_platform.vmx_handler);
40 int i;
41 unsigned long addr, offset;
42 for (i = 0; i < handler->num_slot; i++) {
43 if( type != handler->hdl_list[i].type)
44 continue;
45 addr = handler->hdl_list[i].addr;
46 offset = handler->hdl_list[i].offset;
47 if (p->addr >= addr &&
48 p->addr < addr + offset)
49 return handler->hdl_list[i].action(p);
50 }
51 return 0;
52 }
54 int register_io_handler(unsigned long addr, unsigned long offset,
55 intercept_action_t action, int type)
56 {
57 struct vcpu *d = current;
58 struct vmx_handler_t *handler = &(d->domain->arch.vmx_platform.vmx_handler);
59 int num = handler->num_slot;
61 if (num >= MAX_IO_HANDLER) {
62 printk("no extra space, register io interceptor failed!\n");
63 domain_crash_synchronous();
64 }
66 handler->hdl_list[num].addr = addr;
67 handler->hdl_list[num].offset = offset;
68 handler->hdl_list[num].action = action;
69 handler->hdl_list[num].type = type;
70 handler->num_slot++;
71 return 1;
73 }
75 static void pit_cal_count(struct vmx_virpit_t *vpit)
76 {
77 unsigned int usec_delta = (unsigned int)((NOW() - vpit->inject_point) / 1000);
78 if (usec_delta > vpit->period * 1000)
79 VMX_DBG_LOG(DBG_LEVEL_1, "VMX_PIT:long time has passed from last injection!");
80 vpit->count = vpit->init_val - ((usec_delta * PIT_FREQ / 1000000) % vpit->init_val );
81 }
83 static void pit_latch_io(struct vmx_virpit_t *vpit)
84 {
85 pit_cal_count(vpit);
87 switch(vpit->read_state) {
88 case MSByte:
89 vpit->count_MSB_latched=1;
90 break;
91 case LSByte:
92 vpit->count_LSB_latched=1;
93 break;
94 case LSByte_multiple:
95 vpit->count_LSB_latched=1;
96 vpit->count_MSB_latched=1;
97 break;
98 case MSByte_multiple:
99 VMX_DBG_LOG(DBG_LEVEL_1, "VMX_PIT:latch PIT counter before MSB_multiple!");
100 vpit->read_state=LSByte_multiple;
101 vpit->count_LSB_latched=1;
102 vpit->count_MSB_latched=1;
103 break;
104 default:
105 BUG();
106 }
107 }
109 static int pit_read_io(struct vmx_virpit_t *vpit)
110 {
111 if(vpit->count_LSB_latched) {
112 /* Read Least Significant Byte */
113 if(vpit->read_state==LSByte_multiple) {
114 vpit->read_state=MSByte_multiple;
115 }
116 vpit->count_LSB_latched=0;
117 return (vpit->count & 0xFF);
118 } else if(vpit->count_MSB_latched) {
119 /* Read Most Significant Byte */
120 if(vpit->read_state==MSByte_multiple) {
121 vpit->read_state=LSByte_multiple;
122 }
123 vpit->count_MSB_latched=0;
124 return ((vpit->count>>8) & 0xFF);
125 } else {
126 /* Unlatched Count Read */
127 VMX_DBG_LOG(DBG_LEVEL_1, "VMX_PIT: unlatched read");
128 pit_cal_count(vpit);
129 if(!(vpit->read_state & 0x1)) {
130 /* Read Least Significant Byte */
131 if(vpit->read_state==LSByte_multiple) {
132 vpit->read_state=MSByte_multiple;
133 }
134 return (vpit->count & 0xFF);
135 } else {
136 /* Read Most Significant Byte */
137 if(vpit->read_state==MSByte_multiple) {
138 vpit->read_state=LSByte_multiple;
139 }
140 return ((vpit->count>>8) & 0xFF);
141 }
142 }
143 }
145 /* vmx_io_assist light-weight version, specific to PIT DM */
146 static void resume_pit_io(ioreq_t *p)
147 {
148 struct cpu_user_regs *regs = guest_cpu_user_regs();
149 unsigned long old_eax = regs->eax;
150 p->state = STATE_INVALID;
152 switch(p->size) {
153 case 1:
154 regs->eax = (old_eax & 0xffffff00) | (p->u.data & 0xff);
155 break;
156 case 2:
157 regs->eax = (old_eax & 0xffff0000) | (p->u.data & 0xffff);
158 break;
159 case 4:
160 regs->eax = (p->u.data & 0xffffffff);
161 break;
162 default:
163 BUG();
164 }
165 }
167 /* the intercept action for PIT DM retval:0--not handled; 1--handled */
168 int intercept_pit_io(ioreq_t *p)
169 {
170 struct vcpu *d = current;
171 struct vmx_virpit_t *vpit = &(d->domain->arch.vmx_platform.vmx_pit);
173 if (p->size != 1 ||
174 p->pdata_valid ||
175 p->port_mm)
176 return 0;
178 if (p->addr == 0x43 &&
179 p->dir == 0 && /* write */
180 ((p->u.data >> 4) & 0x3) == 0 && /* latch command */
181 ((p->u.data >> 6) & 0x3) == (vpit->channel)) {/* right channel */
182 pit_latch_io(vpit);
183 return 1;
184 }
186 if (p->addr == (0x40 + vpit->channel) &&
187 p->dir == 1) { /* read */
188 p->u.data = pit_read_io(vpit);
189 resume_pit_io(p);
190 return 1;
191 }
193 return 0;
194 }
196 /* hooks function for the PIT initialization response iopacket */
197 static void pit_timer_fn(void *data)
198 {
199 struct vmx_virpit_t *vpit = data;
201 /* Set the pending intr bit, and send evtchn notification to myself. */
202 if (test_and_set_bit(vpit->vector, vpit->intr_bitmap))
203 vpit->pending_intr_nr++; /* already set, then count the pending intr */
205 set_ac_timer(&vpit->pit_timer, NOW() + MILLISECS(vpit->period));
206 }
209 /* Only some PIT operations such as load init counter need a hypervisor hook.
210 * leave all other operations in user space DM
211 */
212 void vmx_hooks_assist(struct vcpu *d)
213 {
214 vcpu_iodata_t * vio = get_vio(d->domain, d->vcpu_id);
215 ioreq_t *p = &vio->vp_ioreq;
216 shared_iopage_t *sp = get_sp(d->domain);
217 u64 *intr = &(sp->sp_global.pic_intr[0]);
218 struct vmx_virpit_t *vpit = &(d->domain->arch.vmx_platform.vmx_pit);
219 int rw_mode, reinit = 0;
221 /* load init count*/
222 if (p->state == STATE_IORESP_HOOK) {
223 /* set up actimer, handle re-init */
224 if ( active_ac_timer(&(vpit->pit_timer)) ) {
225 VMX_DBG_LOG(DBG_LEVEL_1, "VMX_PIT: guest reset PIT with channel %lx!\n", (unsigned long) ((p->u.data >> 24) & 0x3) );
226 rem_ac_timer(&(vpit->pit_timer));
227 reinit = 1;
228 }
229 else
230 init_ac_timer(&vpit->pit_timer, pit_timer_fn, vpit, 0);
232 /* init count for this channel */
233 vpit->init_val = (p->u.data & 0xFFFF) ;
234 /* frequency(ms) of pit */
235 vpit->period = DIV_ROUND(((vpit->init_val) * 1000), PIT_FREQ);
236 if (vpit->period < 1) {
237 printk("VMX_PIT: guest programmed too small an init_val: %x\n",
238 vpit->init_val);
239 vpit->period = 1;
240 }
241 vpit->vector = ((p->u.data >> 16) & 0xFF);
242 vpit->channel = ((p->u.data >> 24) & 0x3);
243 vpit->first_injected = 0;
245 vpit->count_LSB_latched = 0;
246 vpit->count_MSB_latched = 0;
248 rw_mode = ((p->u.data >> 26) & 0x3);
249 switch(rw_mode) {
250 case 0x1:
251 vpit->read_state=LSByte;
252 break;
253 case 0x2:
254 vpit->read_state=MSByte;
255 break;
256 case 0x3:
257 vpit->read_state=LSByte_multiple;
258 break;
259 default:
260 printk("VMX_PIT:wrong PIT rw_mode!\n");
261 break;
262 }
264 vpit->intr_bitmap = intr;
266 set_ac_timer(&vpit->pit_timer, NOW() + MILLISECS(vpit->period));
268 /*restore the state*/
269 p->state = STATE_IORESP_READY;
271 /* register handler to intercept the PIT io when vm_exit */
272 if (!reinit)
273 register_portio_handler(0x40, 4, intercept_pit_io);
274 }
276 }
278 #endif /* CONFIG_VMX */