ia64/xen-unstable

view xen/arch/x86/hvm/hvm.c @ 12226:45e34f00a78f

[HVM] Clean up VCPU initialisation in Xen. No longer
parse HVM e820 tables in Xen (add some extra HVM parameters as a
cleaner alternative). Lots of code removal.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Thu Nov 02 15:55:51 2006 +0000 (2006-11-02)
parents 7b5115221dfc
children dd62270df2ad
line source
1 /*
2 * hvm.c: Common hardware virtual machine abstractions.
3 *
4 * Copyright (c) 2004, Intel Corporation.
5 * Copyright (c) 2005, International Business Machines 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 */
21 #include <xen/config.h>
22 #include <xen/init.h>
23 #include <xen/lib.h>
24 #include <xen/trace.h>
25 #include <xen/sched.h>
26 #include <xen/irq.h>
27 #include <xen/softirq.h>
28 #include <xen/domain.h>
29 #include <xen/domain_page.h>
30 #include <xen/hypercall.h>
31 #include <xen/guest_access.h>
32 #include <xen/event.h>
33 #include <xen/shadow.h>
34 #include <asm/current.h>
35 #include <asm/e820.h>
36 #include <asm/io.h>
37 #include <asm/shadow.h>
38 #include <asm/regs.h>
39 #include <asm/cpufeature.h>
40 #include <asm/processor.h>
41 #include <asm/types.h>
42 #include <asm/msr.h>
43 #include <asm/mc146818rtc.h>
44 #include <asm/spinlock.h>
45 #include <asm/hvm/hvm.h>
46 #include <asm/hvm/vpt.h>
47 #include <asm/hvm/support.h>
48 #include <public/sched.h>
49 #include <public/hvm/ioreq.h>
50 #include <public/version.h>
51 #include <public/memory.h>
53 int hvm_enabled = 0;
55 unsigned int opt_hvm_debug_level = 0;
56 integer_param("hvm_debug", opt_hvm_debug_level);
58 struct hvm_function_table hvm_funcs;
60 void hvm_create_event_channel(struct vcpu *v)
61 {
62 v->arch.hvm_vcpu.xen_port = alloc_unbound_xen_event_channel(v, 0);
63 if ( get_sp(v->domain) && get_vio(v->domain, v->vcpu_id) )
64 get_vio(v->domain, v->vcpu_id)->vp_eport =
65 v->arch.hvm_vcpu.xen_port;
66 }
68 void hvm_stts(struct vcpu *v)
69 {
70 /* FPU state already dirty? Then no need to setup_fpu() lazily. */
71 if ( test_bit(_VCPUF_fpu_dirtied, &v->vcpu_flags) )
72 return;
74 hvm_funcs.stts(v);
75 }
77 void hvm_set_guest_time(struct vcpu *v, u64 gtime)
78 {
79 u64 host_tsc;
81 rdtscll(host_tsc);
83 v->arch.hvm_vcpu.cache_tsc_offset = gtime - host_tsc;
84 hvm_funcs.set_tsc_offset(v, v->arch.hvm_vcpu.cache_tsc_offset);
85 }
87 void hvm_do_resume(struct vcpu *v)
88 {
89 ioreq_t *p;
90 struct periodic_time *pt =
91 &v->domain->arch.hvm_domain.pl_time.periodic_tm;
93 hvm_stts(v);
95 /* pick up the elapsed PIT ticks and re-enable pit_timer */
96 if ( pt->enabled && v->vcpu_id == pt->bind_vcpu && pt->first_injected ) {
97 if ( v->arch.hvm_vcpu.guest_time ) {
98 hvm_set_guest_time(v, v->arch.hvm_vcpu.guest_time);
99 v->arch.hvm_vcpu.guest_time = 0;
100 }
101 pickup_deactive_ticks(pt);
102 }
104 p = &get_vio(v->domain, v->vcpu_id)->vp_ioreq;
105 wait_on_xen_event_channel(v->arch.hvm_vcpu.xen_port,
106 p->state != STATE_IOREQ_READY &&
107 p->state != STATE_IOREQ_INPROCESS);
108 switch ( p->state )
109 {
110 case STATE_IORESP_READY:
111 hvm_io_assist(v);
112 break;
113 case STATE_INVALID:
114 break;
115 default:
116 printk("Weird HVM iorequest state %d.\n", p->state);
117 domain_crash(v->domain);
118 }
119 }
121 void hvm_release_assist_channel(struct vcpu *v)
122 {
123 free_xen_event_channel(v, v->arch.hvm_vcpu.xen_port);
124 }
127 void hvm_setup_platform(struct domain *d)
128 {
129 struct hvm_domain *platform;
130 struct vcpu *v = current;
132 if ( !is_hvm_domain(d) || (v->vcpu_id != 0) )
133 return;
135 platform = &d->arch.hvm_domain;
136 pic_init(&platform->vpic, pic_irq_request, &platform->interrupt_request);
137 register_pic_io_hook();
139 if ( hvm_apic_support(d) )
140 {
141 spin_lock_init(&d->arch.hvm_domain.round_robin_lock);
142 hvm_vioapic_init(d);
143 }
145 spin_lock_init(&d->arch.hvm_domain.buffered_io_lock);
147 init_timer(&platform->pl_time.periodic_tm.timer,
148 pt_timer_fn, v, v->processor);
149 pit_init(v, cpu_khz);
150 rtc_init(v, RTC_PORT(0), RTC_IRQ);
151 pmtimer_init(v, ACPI_PM_TMR_BLK_ADDRESS);
153 /* init guest tsc to start from 0 */
154 hvm_set_guest_time(v, 0);
155 }
157 void pic_irq_request(void *data, int level)
158 {
159 int *interrupt_request = data;
160 *interrupt_request = level;
161 }
163 void hvm_pic_assist(struct vcpu *v)
164 {
165 global_iodata_t *spg;
166 u16 *virq_line, irqs;
167 struct hvm_virpic *pic = &v->domain->arch.hvm_domain.vpic;
169 spg = &get_sp(v->domain)->sp_global;
170 virq_line = &spg->pic_clear_irr;
171 if ( *virq_line ) {
172 do {
173 irqs = *(volatile u16*)virq_line;
174 } while ( (u16)cmpxchg(virq_line,irqs, 0) != irqs );
175 do_pic_irqs_clear(pic, irqs);
176 }
177 virq_line = &spg->pic_irr;
178 if ( *virq_line ) {
179 do {
180 irqs = *(volatile u16*)virq_line;
181 } while ( (u16)cmpxchg(virq_line,irqs, 0) != irqs );
182 do_pic_irqs(pic, irqs);
183 }
184 }
186 u64 hvm_get_guest_time(struct vcpu *v)
187 {
188 u64 host_tsc;
190 rdtscll(host_tsc);
191 return host_tsc + v->arch.hvm_vcpu.cache_tsc_offset;
192 }
194 int cpu_get_interrupt(struct vcpu *v, int *type)
195 {
196 int intno;
197 struct hvm_virpic *s = &v->domain->arch.hvm_domain.vpic;
198 unsigned long flags;
200 if ( (intno = cpu_get_apic_interrupt(v, type)) != -1 ) {
201 /* set irq request if a PIC irq is still pending */
202 /* XXX: improve that */
203 spin_lock_irqsave(&s->lock, flags);
204 pic_update_irq(s);
205 spin_unlock_irqrestore(&s->lock, flags);
206 return intno;
207 }
208 /* read the irq from the PIC */
209 if ( v->vcpu_id == 0 && (intno = cpu_get_pic_interrupt(v, type)) != -1 )
210 return intno;
212 return -1;
213 }
215 static void hvm_vcpu_down(void)
216 {
217 struct vcpu *v = current;
218 struct domain *d = v->domain;
219 int online_count = 0;
221 gdprintk(XENLOG_INFO, "DOM%d/VCPU%d: going offline.\n",
222 d->domain_id, v->vcpu_id);
224 /* Doesn't halt us immediately, but we'll never return to guest context. */
225 set_bit(_VCPUF_down, &v->vcpu_flags);
226 vcpu_sleep_nosync(v);
228 /* Any other VCPUs online? ... */
229 LOCK_BIGLOCK(d);
230 for_each_vcpu ( d, v )
231 if ( !test_bit(_VCPUF_down, &v->vcpu_flags) )
232 online_count++;
233 UNLOCK_BIGLOCK(d);
235 /* ... Shut down the domain if not. */
236 if ( online_count == 0 )
237 {
238 gdprintk(XENLOG_INFO, "DOM%d: all CPUs offline -- powering off.\n",
239 d->domain_id);
240 domain_shutdown(d, SHUTDOWN_poweroff);
241 }
242 }
244 void hvm_hlt(unsigned long rflags)
245 {
246 struct vcpu *v = current;
247 struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
248 s_time_t next_pt = -1, next_wakeup;
250 /*
251 * If we halt with interrupts disabled, that's a pretty sure sign that we
252 * want to shut down. In a real processor, NMIs are the only way to break
253 * out of this.
254 */
255 if ( unlikely(!(rflags & X86_EFLAGS_IF)) )
256 return hvm_vcpu_down();
258 if ( !v->vcpu_id )
259 next_pt = get_scheduled(v, pt->irq, pt);
260 next_wakeup = get_apictime_scheduled(v);
261 if ( (next_pt != -1 && next_pt < next_wakeup) || next_wakeup == -1 )
262 next_wakeup = next_pt;
263 if ( next_wakeup != - 1 )
264 set_timer(&current->arch.hvm_vcpu.hlt_timer, next_wakeup);
265 do_sched_op_compat(SCHEDOP_block, 0);
266 }
268 /*
269 * __hvm_copy():
270 * @buf = hypervisor buffer
271 * @addr = guest physical address to copy to/from
272 * @size = number of bytes to copy
273 * @dir = copy *to* guest (TRUE) or *from* guest (FALSE)?
274 * Returns number of bytes failed to copy (0 == complete success).
275 */
276 static int __hvm_copy(void *buf, paddr_t addr, int size, int dir)
277 {
278 unsigned long mfn;
279 char *p;
280 int count, todo;
282 todo = size;
283 while ( todo > 0 )
284 {
285 count = min_t(int, PAGE_SIZE - (addr & ~PAGE_MASK), todo);
287 mfn = get_mfn_from_gpfn(addr >> PAGE_SHIFT);
288 if ( mfn == INVALID_MFN )
289 return todo;
291 p = (char *)map_domain_page(mfn) + (addr & ~PAGE_MASK);
293 if ( dir )
294 memcpy(p, buf, count); /* dir == TRUE: *to* guest */
295 else
296 memcpy(buf, p, count); /* dir == FALSE: *from guest */
298 unmap_domain_page(p);
300 addr += count;
301 buf += count;
302 todo -= count;
303 }
305 return 0;
306 }
308 int hvm_copy_to_guest_phys(paddr_t paddr, void *buf, int size)
309 {
310 return __hvm_copy(buf, paddr, size, 1);
311 }
313 int hvm_copy_from_guest_phys(void *buf, paddr_t paddr, int size)
314 {
315 return __hvm_copy(buf, paddr, size, 0);
316 }
318 int hvm_copy_to_guest_virt(unsigned long vaddr, void *buf, int size)
319 {
320 return __hvm_copy(buf, shadow_gva_to_gpa(current, vaddr), size, 1);
321 }
323 int hvm_copy_from_guest_virt(void *buf, unsigned long vaddr, int size)
324 {
325 return __hvm_copy(buf, shadow_gva_to_gpa(current, vaddr), size, 0);
326 }
328 /*
329 * HVM specific printbuf. Mostly used for hvmloader chit-chat.
330 */
331 void hvm_print_line(struct vcpu *v, const char c)
332 {
333 int *index = &v->domain->arch.hvm_domain.pbuf_index;
334 char *pbuf = v->domain->arch.hvm_domain.pbuf;
336 if (*index == HVM_PBUF_SIZE-2 || c == '\n') {
337 if (*index == HVM_PBUF_SIZE-2)
338 pbuf[(*index)++] = c;
339 pbuf[*index] = '\0';
340 printk("(GUEST: %u) %s\n", v->domain->domain_id, pbuf);
341 *index = 0;
342 } else
343 pbuf[(*index)++] = c;
344 }
346 typedef unsigned long hvm_hypercall_t(
347 unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
349 #define HYPERCALL(x) \
350 [ __HYPERVISOR_ ## x ] = (hvm_hypercall_t *) do_ ## x
351 #define HYPERCALL_COMPAT32(x) \
352 [ __HYPERVISOR_ ## x ] = (hvm_hypercall_t *) do_ ## x ## _compat32
354 #if defined(__i386__)
356 static hvm_hypercall_t *hvm_hypercall_table[] = {
357 HYPERCALL(memory_op),
358 HYPERCALL(multicall),
359 HYPERCALL(xen_version),
360 HYPERCALL(event_channel_op),
361 HYPERCALL(hvm_op)
362 };
364 void hvm_do_hypercall(struct cpu_user_regs *pregs)
365 {
366 if ( unlikely(ring_3(pregs)) )
367 {
368 pregs->eax = -EPERM;
369 return;
370 }
372 if ( (pregs->eax >= NR_hypercalls) || !hvm_hypercall_table[pregs->eax] )
373 {
374 gdprintk(XENLOG_INFO, "HVM vcpu %d:%d did a bad hypercall %d.\n",
375 current->domain->domain_id, current->vcpu_id,
376 pregs->eax);
377 pregs->eax = -ENOSYS;
378 return;
379 }
381 pregs->eax = hvm_hypercall_table[pregs->eax](
382 pregs->ebx, pregs->ecx, pregs->edx, pregs->esi, pregs->edi);
383 }
385 #else /* defined(__x86_64__) */
387 static long do_memory_op_compat32(int cmd, XEN_GUEST_HANDLE(void) arg)
388 {
389 extern long do_add_to_physmap(struct xen_add_to_physmap *xatp);
390 long rc;
392 switch ( cmd )
393 {
394 case XENMEM_add_to_physmap:
395 {
396 struct {
397 domid_t domid;
398 uint32_t space;
399 uint32_t idx;
400 uint32_t gpfn;
401 } u;
402 struct xen_add_to_physmap h;
404 if ( copy_from_guest(&u, arg, 1) )
405 return -EFAULT;
407 h.domid = u.domid;
408 h.space = u.space;
409 h.idx = u.idx;
410 h.gpfn = u.gpfn;
412 this_cpu(guest_handles_in_xen_space) = 1;
413 rc = do_memory_op(cmd, guest_handle_from_ptr(&h, void));
414 this_cpu(guest_handles_in_xen_space) = 0;
416 break;
417 }
419 default:
420 gdprintk(XENLOG_INFO, "memory_op %d.\n", cmd);
421 rc = -ENOSYS;
422 break;
423 }
425 return rc;
426 }
428 static hvm_hypercall_t *hvm_hypercall64_table[NR_hypercalls] = {
429 HYPERCALL(memory_op),
430 HYPERCALL(xen_version),
431 HYPERCALL(hvm_op),
432 HYPERCALL(event_channel_op)
433 };
435 static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = {
436 HYPERCALL_COMPAT32(memory_op),
437 HYPERCALL(xen_version),
438 HYPERCALL(hvm_op),
439 HYPERCALL(event_channel_op)
440 };
442 void hvm_do_hypercall(struct cpu_user_regs *pregs)
443 {
444 if ( unlikely(ring_3(pregs)) )
445 {
446 pregs->rax = -EPERM;
447 return;
448 }
450 pregs->rax = (uint32_t)pregs->eax; /* mask in case compat32 caller */
451 if ( (pregs->rax >= NR_hypercalls) || !hvm_hypercall64_table[pregs->rax] )
452 {
453 gdprintk(XENLOG_INFO, "HVM vcpu %d:%d did a bad hypercall %ld.\n",
454 current->domain->domain_id, current->vcpu_id,
455 pregs->rax);
456 pregs->rax = -ENOSYS;
457 return;
458 }
460 if ( current->arch.shadow.mode->guest_levels == 4 )
461 {
462 pregs->rax = hvm_hypercall64_table[pregs->rax](pregs->rdi,
463 pregs->rsi,
464 pregs->rdx,
465 pregs->r10,
466 pregs->r8);
467 }
468 else
469 {
470 pregs->eax = hvm_hypercall32_table[pregs->eax]((uint32_t)pregs->ebx,
471 (uint32_t)pregs->ecx,
472 (uint32_t)pregs->edx,
473 (uint32_t)pregs->esi,
474 (uint32_t)pregs->edi);
475 }
476 }
478 #endif /* defined(__x86_64__) */
480 /* Initialise a hypercall transfer page for a VMX domain using
481 paravirtualised drivers. */
482 void hvm_hypercall_page_initialise(struct domain *d,
483 void *hypercall_page)
484 {
485 hvm_funcs.init_hypercall_page(d, hypercall_page);
486 }
489 /*
490 * only called in HVM domain BSP context
491 * when booting, vcpuid is always equal to apic_id
492 */
493 int hvm_bringup_ap(int vcpuid, int trampoline_vector)
494 {
495 struct vcpu *bsp = current, *v;
496 struct domain *d = bsp->domain;
497 struct vcpu_guest_context *ctxt;
498 int rc = 0;
500 BUG_ON(!is_hvm_domain(d));
502 if ( bsp->vcpu_id != 0 )
503 {
504 gdprintk(XENLOG_ERR, "Not calling hvm_bringup_ap from BSP context.\n");
505 domain_crash_synchronous();
506 }
508 if ( (v = d->vcpu[vcpuid]) == NULL )
509 return -ENOENT;
511 if ( (ctxt = xmalloc(struct vcpu_guest_context)) == NULL )
512 {
513 gdprintk(XENLOG_INFO,
514 "Failed to allocate memory in hvm_bringup_ap.\n");
515 return -ENOMEM;
516 }
518 hvm_init_ap_context(ctxt, vcpuid, trampoline_vector);
520 LOCK_BIGLOCK(d);
521 rc = -EEXIST;
522 if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
523 rc = boot_vcpu(d, vcpuid, ctxt);
524 UNLOCK_BIGLOCK(d);
526 if ( rc != 0 )
527 {
528 gdprintk(XENLOG_INFO,
529 "AP %d bringup failed in boot_vcpu %x.\n", vcpuid, rc);
530 goto out;
531 }
533 if ( test_and_clear_bit(_VCPUF_down, &d->vcpu[vcpuid]->vcpu_flags) )
534 vcpu_wake(d->vcpu[vcpuid]);
535 gdprintk(XENLOG_INFO, "AP %d bringup suceeded.\n", vcpuid);
537 out:
538 xfree(ctxt);
539 return rc;
540 }
542 long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
544 {
545 long rc = 0;
547 switch ( op )
548 {
549 case HVMOP_set_param:
550 case HVMOP_get_param:
551 {
552 struct xen_hvm_param a;
553 struct domain *d;
554 struct vcpu *v;
555 unsigned long mfn;
556 void *p;
558 if ( copy_from_guest(&a, arg, 1) )
559 return -EFAULT;
561 if ( a.index >= HVM_NR_PARAMS )
562 return -EINVAL;
564 if ( a.domid == DOMID_SELF )
565 {
566 get_knownalive_domain(current->domain);
567 d = current->domain;
568 }
569 else if ( IS_PRIV(current->domain) )
570 {
571 d = find_domain_by_id(a.domid);
572 if ( d == NULL )
573 return -ESRCH;
574 }
575 else
576 {
577 return -EPERM;
578 }
580 rc = -EINVAL;
581 if ( !is_hvm_domain(d) )
582 goto param_fail;
584 if ( op == HVMOP_set_param )
585 {
586 switch ( a.index )
587 {
588 case HVM_PARAM_IOREQ_PFN:
589 if ( d->arch.hvm_domain.shared_page_va )
590 goto param_fail;
591 mfn = gmfn_to_mfn(d, a.value);
592 if ( mfn == INVALID_MFN )
593 goto param_fail;
594 p = map_domain_page_global(mfn);
595 if ( p == NULL )
596 goto param_fail;
597 d->arch.hvm_domain.shared_page_va = (unsigned long)p;
598 /* Initialise evtchn port info if VCPUs already created. */
599 for_each_vcpu ( d, v )
600 get_vio(d, v->vcpu_id)->vp_eport =
601 v->arch.hvm_vcpu.xen_port;
602 break;
603 case HVM_PARAM_BUFIOREQ_PFN:
604 if ( d->arch.hvm_domain.buffered_io_va )
605 goto param_fail;
606 mfn = gmfn_to_mfn(d, a.value);
607 if ( mfn == INVALID_MFN )
608 goto param_fail;
609 p = map_domain_page_global(mfn);
610 if ( p == NULL )
611 goto param_fail;
612 d->arch.hvm_domain.buffered_io_va = (unsigned long)p;
613 break;
614 }
615 d->arch.hvm_domain.params[a.index] = a.value;
616 rc = 0;
617 }
618 else
619 {
620 a.value = d->arch.hvm_domain.params[a.index];
621 rc = copy_to_guest(arg, &a, 1) ? -EFAULT : 0;
622 }
624 param_fail:
625 put_domain(d);
626 break;
627 }
629 default:
630 {
631 gdprintk(XENLOG_INFO, "Bad HVM op %ld.\n", op);
632 rc = -ENOSYS;
633 break;
634 }
635 }
637 return rc;
638 }
640 /*
641 * Local variables:
642 * mode: C
643 * c-set-style: "BSD"
644 * c-basic-offset: 4
645 * tab-width: 4
646 * indent-tabs-mode: nil
647 * End:
648 */