ia64/xen-unstable

view xen/arch/ia64/vmx/vlsapic.c @ 9157:a693ccb4d581

[IA64] VTI: fix Oops: time tick before it's due

1. Guest may set itm several times in one execution of timer handler of
guest. VMM need to handle this situation.
2. VMM don't need to stop guest timer when switching out and rest guest
timer when switching in, this may make room for some corner case, I don't
figure out this kind of corner cases now :-), I just removed this logic.
3. When VMM emulate writing itv, VMM can't simply stop timer, when guest
is masked.
4. All operations such as read/write itv, itc, itm don't need to disable
interrupt, due to there is no conflict access.

After all these modifications, VTIdomain don't complain "Oops: time tick
before it's due", I don't do the full test:-).

Signed-off-by: Anthony Xu <anthony.xu@intel.com>
author awilliam@xenbuild.aw
date Tue Mar 07 20:01:29 2006 -0700 (2006-03-07)
parents c18c63f87b7d
children 8a551ec13d93
line source
2 /* -*- Mode:C; c-basic-offset:4; tab-width:4; indent-tabs-mode:nil -*- */
3 /*
4 * vlsapic.c: virtual lsapic model including ITC timer.
5 * Copyright (c) 2005, Intel Corporation.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 * Place - Suite 330, Boston, MA 02111-1307 USA.
19 *
20 * Yaozu Dong (Eddie Dong) (Eddie.dong@intel.com)
21 */
23 #include <linux/sched.h>
24 #include <public/arch-ia64.h>
25 #include <asm/ia64_int.h>
26 #include <asm/vcpu.h>
27 #include <asm/regionreg.h>
28 #include <asm/tlb.h>
29 #include <asm/processor.h>
30 #include <asm/delay.h>
31 #include <asm/vmx_vcpu.h>
32 #include <asm/vmx_vcpu.h>
33 #include <asm/regs.h>
34 #include <asm/gcc_intrin.h>
35 #include <asm/vmx_mm_def.h>
36 #include <asm/vmx.h>
37 #include <asm/hw_irq.h>
38 #include <asm/vmx_pal_vsa.h>
39 #include <asm/kregs.h>
40 #include <asm/vmx_platform.h>
41 #include <asm/hvm/vioapic.h>
43 //u64 fire_itc;
44 //u64 fire_itc2;
45 //u64 fire_itm;
46 //u64 fire_itm2;
47 /*
48 * Update the checked last_itc.
49 */
51 extern void vmx_reflect_interruption(UINT64 ifa,UINT64 isr,UINT64 iim,
52 UINT64 vector,REGS *regs);
53 static void update_last_itc(vtime_t *vtm, uint64_t cur_itc)
54 {
55 vtm->last_itc = cur_itc;
56 }
58 /*
59 * ITC value saw in guest (host+offset+drift).
60 */
61 static uint64_t now_itc(vtime_t *vtm)
62 {
63 uint64_t guest_itc=vtm->vtm_offset+ia64_get_itc();
65 if ( vtm->vtm_local_drift ) {
66 // guest_itc -= vtm->vtm_local_drift;
67 }
68 if ( (long)(guest_itc - vtm->last_itc) > 0 ) {
69 return guest_itc;
71 }
72 else {
73 /* guest ITC backwarded due after LP switch */
74 return vtm->last_itc;
75 }
76 }
78 /*
79 * Interval time components reset.
80 */
81 static void vtm_reset(VCPU *vcpu)
82 {
83 uint64_t cur_itc;
84 vtime_t *vtm;
86 vtm=&(vcpu->arch.arch_vmx.vtm);
87 vtm->vtm_offset = 0;
88 vtm->vtm_local_drift = 0;
89 VCPU(vcpu, itm) = 0;
90 VCPU(vcpu, itv) = 0x10000;
91 cur_itc = ia64_get_itc();
92 vtm->last_itc = vtm->vtm_offset + cur_itc;
93 }
95 /* callback function when vtm_timer expires */
96 static void vtm_timer_fn(void *data)
97 {
98 vtime_t *vtm;
99 VCPU *vcpu = data;
100 u64 cur_itc,vitv;
102 vitv = VCPU(vcpu, itv);
103 if ( !ITV_IRQ_MASK(vitv) ){
104 vmx_vcpu_pend_interrupt(vcpu, vitv & 0xff);
105 }
106 vtm=&(vcpu->arch.arch_vmx.vtm);
107 cur_itc = now_itc(vtm);
108 // vitm =VCPU(vcpu, itm);
109 //fire_itc2 = cur_itc;
110 //fire_itm2 = vitm;
111 update_last_itc(vtm,cur_itc); // pseudo read to update vITC
112 }
114 void vtm_init(VCPU *vcpu)
115 {
116 vtime_t *vtm;
117 uint64_t itc_freq;
119 vtm=&(vcpu->arch.arch_vmx.vtm);
121 itc_freq = local_cpu_data->itc_freq;
122 vtm->cfg_max_jump=itc_freq*MAX_JUMP_STEP/1000;
123 vtm->cfg_min_grun=itc_freq*MIN_GUEST_RUNNING_TIME/1000;
124 init_timer(&vtm->vtm_timer, vtm_timer_fn, vcpu, 0);
125 vtm_reset(vcpu);
126 }
128 /*
129 * Action when guest read ITC.
130 */
131 uint64_t vtm_get_itc(VCPU *vcpu)
132 {
133 uint64_t guest_itc, spsr;
134 vtime_t *vtm;
136 vtm=&(vcpu->arch.arch_vmx.vtm);
137 guest_itc = now_itc(vtm);
138 return guest_itc;
139 }
144 void vtm_set_itc(VCPU *vcpu, uint64_t new_itc)
145 {
146 uint64_t vitm, vitv;
147 vtime_t *vtm;
148 vitm = VCPU(vcpu,itm);
149 vitv = VCPU(vcpu,itv);
150 vtm=&(vcpu->arch.arch_vmx.vtm);
151 vtm->vtm_offset = new_itc - ia64_get_itc();
152 vtm->last_itc = new_itc;
153 if(vitm < new_itc){
154 clear_bit(ITV_VECTOR(vitv), &VCPU(vcpu, irr[0]));
155 stop_timer(&vtm->vtm_timer);
156 }
157 }
160 #define TIMER_SLOP (50*1000) /* ns */ /* copy from timer.c */
161 extern u64 cycle_to_ns(u64 cyle);
164 void vtm_set_itm(VCPU *vcpu, uint64_t val)
165 {
166 vtime_t *vtm;
167 uint64_t vitv, cur_itc, expires;
168 vitv = VCPU(vcpu, itv);
169 vtm=&(vcpu->arch.arch_vmx.vtm);
170 // TODO; need to handle VHPI in future
171 clear_bit(ITV_VECTOR(vitv), &VCPU(vcpu, irr[0]));
172 VCPU(vcpu,itm)=val;
173 cur_itc =now_itc(vtm);
174 if(val > vtm->last_itc){
175 expires = NOW() + cycle_to_ns(val-cur_itc) + TIMER_SLOP;
176 set_timer(&vtm->vtm_timer, expires);
177 }else{
178 stop_timer(&vtm->vtm_timer);
179 }
180 }
183 void vtm_set_itv(VCPU *vcpu, uint64_t val)
184 {
185 uint64_t olditv;
186 olditv = VCPU(vcpu, itv);
187 VCPU(vcpu, itv) = val;
188 if(ITV_IRQ_MASK(val)){
189 clear_bit(ITV_VECTOR(olditv), &VCPU(vcpu, irr[0]));
190 }else if(ITV_VECTOR(olditv)!=ITV_VECTOR(val)){
191 if(test_and_clear_bit(ITV_VECTOR(olditv), &VCPU(vcpu, irr[0])))
192 set_bit(ITV_VECTOR(val), &VCPU(vcpu, irr[0]));
193 }
194 }
197 /*
198 * Update interrupt or hook the vtm timer for fire
199 * At this point vtm_timer should be removed if itv is masked.
200 */
201 /* Interrupt must be disabled at this point */
202 /*
203 void vtm_interruption_update(VCPU *vcpu, vtime_t* vtm)
204 {
205 uint64_t cur_itc,vitm,vitv;
206 uint64_t expires;
207 long diff_now, diff_last;
208 uint64_t spsr;
210 vitv = VCPU(vcpu, itv);
211 if ( ITV_IRQ_MASK(vitv) ) {
212 return;
213 }
215 vitm =VCPU(vcpu, itm);
216 local_irq_save(spsr);
217 cur_itc =now_itc(vtm);
218 diff_last = vtm->last_itc - vitm;
219 diff_now = cur_itc - vitm;
221 if ( diff_last >= 0 ) {
222 // interrupt already fired.
223 stop_timer(&vtm->vtm_timer);
224 }
225 else if ( diff_now >= 0 ) {
226 // ITV is fired.
227 vmx_vcpu_pend_interrupt(vcpu, vitv&0xff);
228 }
229 */
230 /* Both last_itc & cur_itc < itm, wait for fire condition */
231 /* else {
232 expires = NOW() + cycle_to_ns(0-diff_now) + TIMER_SLOP;
233 set_timer(&vtm->vtm_timer, expires);
234 }
235 local_irq_restore(spsr);
236 }
237 */
239 /*
240 * Action for vtm when the domain is scheduled out.
241 * Remove the timer for vtm.
242 */
243 /*
244 void vtm_domain_out(VCPU *vcpu)
245 {
246 if(!is_idle_domain(vcpu->domain))
247 stop_timer(&vcpu->arch.arch_vmx.vtm.vtm_timer);
248 }
249 */
250 /*
251 * Action for vtm when the domain is scheduled in.
252 * Fire vtm IRQ or add the timer for vtm.
253 */
254 /*
255 void vtm_domain_in(VCPU *vcpu)
256 {
257 vtime_t *vtm;
259 if(!is_idle_domain(vcpu->domain)) {
260 vtm=&(vcpu->arch.arch_vmx.vtm);
261 vtm_interruption_update(vcpu, vtm);
262 }
263 }
264 */
266 /*
267 * Next for vLSapic
268 */
270 #define NMI_VECTOR 2
271 #define ExtINT_VECTOR 0
272 #define NULL_VECTOR -1
273 static void update_vhpi(VCPU *vcpu, int vec)
274 {
275 u64 vhpi;
276 if ( vec == NULL_VECTOR ) {
277 vhpi = 0;
278 }
279 else if ( vec == NMI_VECTOR ) { // NMI
280 vhpi = 32;
281 } else if (vec == ExtINT_VECTOR) { //ExtINT
282 vhpi = 16;
283 }
284 else {
285 vhpi = vec / 16;
286 }
288 VCPU(vcpu,vhpi) = vhpi;
289 // TODO: Add support for XENO
290 if ( VCPU(vcpu,vac).a_int ) {
291 ia64_call_vsa ( PAL_VPS_SET_PENDING_INTERRUPT,
292 (uint64_t) &(vcpu->arch.privregs), 0, 0,0,0,0,0);
293 }
294 }
296 #ifdef V_IOSAPIC_READY
297 /* Assist to check virtual interrupt lines */
298 void vmx_virq_line_assist(struct vcpu *v)
299 {
300 global_iodata_t *spg = &get_sp(v->domain)->sp_global;
301 uint16_t *virq_line, irqs;
303 virq_line = &spg->pic_irr;
304 if (*virq_line) {
305 do {
306 irqs = *(volatile uint16_t*)virq_line;
307 } while ((uint16_t)cmpxchg(virq_line, irqs, 0) != irqs);
308 hvm_vioapic_do_irqs(v->domain, irqs);
309 }
311 virq_line = &spg->pic_clear_irr;
312 if (*virq_line) {
313 do {
314 irqs = *(volatile uint16_t*)virq_line;
315 } while ((uint16_t)cmpxchg(virq_line, irqs, 0) != irqs);
316 hvm_vioapic_do_irqs_clear(v->domain, irqs);
317 }
318 }
320 void vmx_virq_line_init(struct domain *d)
321 {
322 global_iodata_t *spg = &get_sp(d)->sp_global;
324 spg->pic_elcr = 0xdef8; /* Level/Edge trigger mode */
325 spg->pic_irr = 0;
326 spg->pic_last_irr = 0;
327 spg->pic_clear_irr = 0;
328 }
330 int ioapic_match_logical_addr(hvm_vioapic_t *s, int number, uint16_t dest)
331 {
332 return (VLAPIC_ID(s->lapic_info[number]) == dest);
333 }
335 struct vlapic* apic_round_robin(struct domain *d,
336 uint8_t dest_mode,
337 uint8_t vector,
338 uint32_t bitmap)
339 {
340 uint8_t bit;
341 hvm_vioapic_t *s;
343 if (!bitmap) {
344 printk("<apic_round_robin> no bit on bitmap\n");
345 return NULL;
346 }
348 s = &d->arch.vmx_platform.vioapic;
349 for (bit = 0; bit < s->lapic_count; bit++) {
350 if (bitmap & (1 << bit))
351 return s->lapic_info[bit];
352 }
354 return NULL;
355 }
356 #endif
358 void vlsapic_reset(VCPU *vcpu)
359 {
360 int i;
362 VCPU(vcpu, lid) = ia64_getreg(_IA64_REG_CR_LID);
363 VCPU(vcpu, ivr) = 0;
364 VCPU(vcpu,tpr) = 0x10000;
365 VCPU(vcpu, eoi) = 0;
366 VCPU(vcpu, irr[0]) = 0;
367 VCPU(vcpu, irr[1]) = 0;
368 VCPU(vcpu, irr[2]) = 0;
369 VCPU(vcpu, irr[3]) = 0;
370 VCPU(vcpu, pmv) = 0x10000;
371 VCPU(vcpu, cmcv) = 0x10000;
372 VCPU(vcpu, lrr0) = 0x10000; // default reset value?
373 VCPU(vcpu, lrr1) = 0x10000; // default reset value?
374 update_vhpi(vcpu, NULL_VECTOR);
375 for ( i=0; i<4; i++) {
376 VLSAPIC_INSVC(vcpu,i) = 0;
377 }
379 #ifdef V_IOSAPIC_READY
380 vcpu->arch.arch_vmx.vlapic.vcpu = vcpu;
381 hvm_vioapic_add_lapic(&vcpu->arch.arch_vmx.vlapic, vcpu);
382 #endif
383 DPRINTK("VLSAPIC inservice base=%lp\n", &VLSAPIC_INSVC(vcpu,0) );
384 }
386 /*
387 * Find highest signaled bits in 4 words (long).
388 *
389 * return 0-255: highest bits.
390 * -1 : Not found.
391 */
392 static __inline__ int highest_bits(uint64_t *dat)
393 {
394 uint64_t bits, bitnum;
395 int i;
397 /* loop for all 256 bits */
398 for ( i=3; i >= 0 ; i -- ) {
399 bits = dat[i];
400 if ( bits ) {
401 bitnum = ia64_fls(bits);
402 return i*64+bitnum;
403 }
404 }
405 return NULL_VECTOR;
406 }
408 /*
409 * Return 0-255 for pending irq.
410 * NULL_VECTOR: when no pending.
411 */
412 static int highest_pending_irq(VCPU *vcpu)
413 {
414 if ( VCPU(vcpu, irr[0]) & (1UL<<NMI_VECTOR) ) return NMI_VECTOR;
415 if ( VCPU(vcpu, irr[0]) & (1UL<<ExtINT_VECTOR) ) return ExtINT_VECTOR;
416 return highest_bits(&VCPU(vcpu, irr[0]));
417 }
419 static int highest_inservice_irq(VCPU *vcpu)
420 {
421 if ( VLSAPIC_INSVC(vcpu, 0) & (1UL<<NMI_VECTOR) ) return NMI_VECTOR;
422 if ( VLSAPIC_INSVC(vcpu, 0) & (1UL<<ExtINT_VECTOR) ) return ExtINT_VECTOR;
423 return highest_bits(&(VLSAPIC_INSVC(vcpu, 0)));
424 }
426 /*
427 * The pending irq is higher than the inservice one.
428 *
429 */
430 static int is_higher_irq(int pending, int inservice)
431 {
432 return ( (pending >> 4) > (inservice>>4) ||
433 ((pending != NULL_VECTOR) && (inservice == NULL_VECTOR)) );
434 }
436 static int is_higher_class(int pending, int mic)
437 {
438 return ( (pending >> 4) > mic );
439 }
441 static int is_invalid_irq(int vec)
442 {
443 return (vec == 1 || ((vec <= 14 && vec >= 3)));
444 }
446 #define IRQ_NO_MASKED 0
447 #define IRQ_MASKED_BY_VTPR 1
448 #define IRQ_MASKED_BY_INSVC 2 // masked by inservice IRQ
450 /* See Table 5-8 in SDM vol2 for the definition */
451 static int
452 _xirq_masked(VCPU *vcpu, int h_pending, int h_inservice)
453 {
454 tpr_t vtpr;
455 uint64_t mmi;
457 vtpr.val = VCPU(vcpu, tpr);
459 if ( h_inservice == NMI_VECTOR ) {
460 return IRQ_MASKED_BY_INSVC;
461 }
462 if ( h_pending == NMI_VECTOR ) {
463 // Non Maskable Interrupt
464 return IRQ_NO_MASKED;
465 }
466 if ( h_inservice == ExtINT_VECTOR ) {
467 return IRQ_MASKED_BY_INSVC;
468 }
469 mmi = vtpr.mmi;
470 if ( h_pending == ExtINT_VECTOR ) {
471 if ( mmi ) {
472 // mask all external IRQ
473 return IRQ_MASKED_BY_VTPR;
474 }
475 else {
476 return IRQ_NO_MASKED;
477 }
478 }
480 if ( is_higher_irq(h_pending, h_inservice) ) {
481 if ( !mmi && is_higher_class(h_pending, vtpr.mic) ) {
482 return IRQ_NO_MASKED;
483 }
484 else {
485 return IRQ_MASKED_BY_VTPR;
486 }
487 }
488 else {
489 return IRQ_MASKED_BY_INSVC;
490 }
491 }
493 static int irq_masked(VCPU *vcpu, int h_pending, int h_inservice)
494 {
495 int mask;
497 mask = _xirq_masked(vcpu, h_pending, h_inservice);
498 return mask;
499 }
502 /*
503 * May come from virtualization fault or
504 * nested host interrupt.
505 */
506 int vmx_vcpu_pend_interrupt(VCPU *vcpu, uint8_t vector)
507 {
508 uint64_t spsr;
509 int ret;
511 if (vector & ~0xff) {
512 DPRINTK("vmx_vcpu_pend_interrupt: bad vector\n");
513 return -1;
514 }
515 local_irq_save(spsr);
516 ret = test_and_set_bit(vector, &VCPU(vcpu, irr[0]));
517 local_irq_restore(spsr);
518 vcpu->arch.irq_new_pending = 1;
519 return ret;
520 }
522 /*
523 * Add batch of pending interrupt.
524 * The interrupt source is contained in pend_irr[0-3] with
525 * each bits stand for one interrupt.
526 */
527 void vmx_vcpu_pend_batch_interrupt(VCPU *vcpu, UINT64 *pend_irr)
528 {
529 uint64_t spsr;
530 int i;
532 local_irq_save(spsr);
533 for (i=0 ; i<4; i++ ) {
534 VCPU(vcpu,irr[i]) |= pend_irr[i];
535 }
536 local_irq_restore(spsr);
537 vcpu->arch.irq_new_pending = 1;
538 }
540 /*
541 * If the new pending interrupt is enabled and not masked, we directly inject
542 * it into the guest. Otherwise, we set the VHPI if vac.a_int=1 so that when
543 * the interrupt becomes unmasked, it gets injected.
544 * RETURN:
545 * TRUE: Interrupt is injected.
546 * FALSE: Not injected but may be in VHPI when vac.a_int=1
547 *
548 * Optimization: We defer setting the VHPI until the EOI time, if a higher
549 * priority interrupt is in-service. The idea is to reduce the
550 * number of unnecessary calls to inject_vhpi.
551 */
552 int vmx_check_pending_irq(VCPU *vcpu)
553 {
554 uint64_t spsr, mask;
555 int h_pending, h_inservice;
556 int injected=0;
557 uint64_t isr;
558 IA64_PSR vpsr;
559 REGS *regs=vcpu_regs(vcpu);
560 local_irq_save(spsr);
561 h_pending = highest_pending_irq(vcpu);
562 if ( h_pending == NULL_VECTOR ) goto chk_irq_exit;
563 h_inservice = highest_inservice_irq(vcpu);
565 vpsr.val = vmx_vcpu_get_psr(vcpu);
566 mask = irq_masked(vcpu, h_pending, h_inservice);
567 if ( vpsr.i && IRQ_NO_MASKED == mask ) {
568 isr = vpsr.val & IA64_PSR_RI;
569 if ( !vpsr.ic )
570 panic("Interrupt when IC=0\n");
571 vmx_reflect_interruption(0,isr,0, 12, regs ); // EXT IRQ
572 injected = 1;
573 }
574 else if ( mask == IRQ_MASKED_BY_INSVC ) {
575 // cann't inject VHPI
576 // DPRINTK("IRQ masked by higher inservice\n");
577 }
578 else {
579 // masked by vpsr.i or vtpr.
580 update_vhpi(vcpu,h_pending);
581 }
583 chk_irq_exit:
584 local_irq_restore(spsr);
585 return injected;
586 }
588 /*
589 * Only coming from virtualization fault.
590 */
591 void guest_write_eoi(VCPU *vcpu)
592 {
593 int vec;
594 uint64_t spsr;
596 vec = highest_inservice_irq(vcpu);
597 if ( vec == NULL_VECTOR ) panic("Wrong vector to EOI\n");
598 local_irq_save(spsr);
599 VLSAPIC_INSVC(vcpu,vec>>6) &= ~(1UL <<(vec&63));
600 local_irq_restore(spsr);
601 VCPU(vcpu, eoi)=0; // overwrite the data
602 vcpu->arch.irq_new_pending=1;
603 // vmx_check_pending_irq(vcpu);
604 }
606 uint64_t guest_read_vivr(VCPU *vcpu)
607 {
608 int vec, h_inservice;
609 uint64_t spsr;
611 local_irq_save(spsr);
612 vec = highest_pending_irq(vcpu);
613 h_inservice = highest_inservice_irq(vcpu);
614 if ( vec == NULL_VECTOR ||
615 irq_masked(vcpu, vec, h_inservice) != IRQ_NO_MASKED ) {
616 local_irq_restore(spsr);
617 return IA64_SPURIOUS_INT_VECTOR;
618 }
620 VLSAPIC_INSVC(vcpu,vec>>6) |= (1UL <<(vec&63));
621 VCPU(vcpu, irr[vec>>6]) &= ~(1UL <<(vec&63));
622 update_vhpi(vcpu, NULL_VECTOR); // clear VHPI till EOI or IRR write
623 local_irq_restore(spsr);
624 return (uint64_t)vec;
625 }
627 static void generate_exirq(VCPU *vcpu)
628 {
629 IA64_PSR vpsr;
630 uint64_t isr;
631 REGS *regs=vcpu_regs(vcpu);
632 vpsr.val = vmx_vcpu_get_psr(vcpu);
633 update_vhpi(vcpu, NULL_VECTOR);
634 isr = vpsr.val & IA64_PSR_RI;
635 if ( !vpsr.ic )
636 panic("Interrupt when IC=0\n");
637 vmx_reflect_interruption(0,isr,0, 12, regs); // EXT IRQ
638 }
640 void vhpi_detection(VCPU *vcpu)
641 {
642 uint64_t threshold,vhpi;
643 tpr_t vtpr;
644 IA64_PSR vpsr;
646 vpsr.val = vmx_vcpu_get_psr(vcpu);
647 vtpr.val = VCPU(vcpu, tpr);
649 threshold = ((!vpsr.i) << 5) | (vtpr.mmi << 4) | vtpr.mic;
650 vhpi = VCPU(vcpu,vhpi);
651 if ( vhpi > threshold ) {
652 // interrupt actived
653 generate_exirq (vcpu);
654 }
655 }
657 void vmx_vexirq(VCPU *vcpu)
658 {
659 static uint64_t vexirq_count=0;
661 vexirq_count ++;
662 printk("Virtual ex-irq %ld\n", vexirq_count);
663 generate_exirq (vcpu);
664 }