/****************************************************************************** * arch/x86/domain.c * * x86-specific domain handling (e.g., register setup and context switching). */ /* * Copyright (C) 1995 Linus Torvalds * * Pentium III FXSR, SSE support * Gareth Hughes , May 2000 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_COMPAT #include #endif DEFINE_PER_CPU(struct vcpu *, curr_vcpu); DEFINE_PER_CPU(u64, efer); DEFINE_PER_CPU(unsigned long, cr4); static void default_idle(void); void (*pm_idle) (void) = default_idle; static void paravirt_ctxt_switch_from(struct vcpu *v); static void paravirt_ctxt_switch_to(struct vcpu *v); static void vcpu_destroy_pagetables(struct vcpu *v); static void continue_idle_domain(struct vcpu *v) { reset_stack_and_jump(idle_loop); } static void continue_nonidle_domain(struct vcpu *v) { reset_stack_and_jump(ret_from_intr); } static void default_idle(void) { local_irq_disable(); if ( !softirq_pending(smp_processor_id()) ) safe_halt(); else local_irq_enable(); } static void play_dead(void) { /* * Flush pending softirqs if any. They can be queued up before this CPU * was taken out of cpu_online_map in __cpu_disable(). */ do_softirq(); /* This must be done before dead CPU ack */ cpu_exit_clear(); hvm_cpu_down(); wbinvd(); mb(); /* Ack it */ __get_cpu_var(cpu_state) = CPU_DEAD; /* With physical CPU hotplug, we should halt the cpu. */ local_irq_disable(); for ( ; ; ) halt(); } void idle_loop(void) { for ( ; ; ) { if ( cpu_is_offline(smp_processor_id()) ) play_dead(); page_scrub_schedule_work(); (*pm_idle)(); do_softirq(); } } void startup_cpu_idle_loop(void) { struct vcpu *v = current; ASSERT(is_idle_vcpu(v)); cpu_set(smp_processor_id(), v->domain->domain_dirty_cpumask); cpu_set(smp_processor_id(), v->vcpu_dirty_cpumask); reset_stack_and_jump(idle_loop); } void dump_pageframe_info(struct domain *d) { struct page_info *page; printk("Memory pages belonging to domain %u:\n", d->domain_id); if ( d->tot_pages >= 10 ) { printk(" DomPage list too long to display\n"); } else { list_for_each_entry ( page, &d->page_list, list ) { printk(" DomPage %p: caf=%08x, taf=%" PRtype_info "\n", _p(page_to_mfn(page)), page->count_info, page->u.inuse.type_info); } } list_for_each_entry ( page, &d->xenpage_list, list ) { printk(" XenPage %p: caf=%08x, taf=%" PRtype_info "\n", _p(page_to_mfn(page)), page->count_info, page->u.inuse.type_info); } } struct vcpu *alloc_vcpu_struct(void) { struct vcpu *v; if ( (v = xmalloc(struct vcpu)) != NULL ) memset(v, 0, sizeof(*v)); return v; } void free_vcpu_struct(struct vcpu *v) { xfree(v); } #ifdef CONFIG_COMPAT static int setup_compat_l4(struct vcpu *v) { struct page_info *pg = alloc_domheap_page(NULL, 0); l4_pgentry_t *l4tab; if ( pg == NULL ) return -ENOMEM; /* This page needs to look like a pagetable so that it can be shadowed */ pg->u.inuse.type_info = PGT_l4_page_table|PGT_validated|1; l4tab = copy_page(page_to_virt(pg), idle_pg_table); l4tab[0] = l4e_empty(); l4tab[l4_table_offset(LINEAR_PT_VIRT_START)] = l4e_from_page(pg, __PAGE_HYPERVISOR); l4tab[l4_table_offset(PERDOMAIN_VIRT_START)] = l4e_from_paddr(__pa(v->domain->arch.mm_perdomain_l3), __PAGE_HYPERVISOR); v->arch.guest_table = pagetable_from_page(pg); v->arch.guest_table_user = v->arch.guest_table; return 0; } static void release_compat_l4(struct vcpu *v) { free_domheap_page(pagetable_get_page(v->arch.guest_table)); v->arch.guest_table = pagetable_null(); v->arch.guest_table_user = pagetable_null(); } static inline int may_switch_mode(struct domain *d) { return (!is_hvm_domain(d) && (d->tot_pages == 0)); } int switch_native(struct domain *d) { unsigned int vcpuid; if ( d == NULL ) return -EINVAL; if ( !may_switch_mode(d) ) return -EACCES; if ( !is_pv_32on64_domain(d) ) return 0; d->arch.is_32bit_pv = d->arch.has_32bit_shinfo = 0; for ( vcpuid = 0; vcpuid < MAX_VIRT_CPUS; vcpuid++ ) { if (d->vcpu[vcpuid]) release_compat_l4(d->vcpu[vcpuid]); } return 0; } int switch_compat(struct domain *d) { unsigned int vcpuid; if ( d == NULL ) return -EINVAL; if ( !may_switch_mode(d) ) return -EACCES; if ( is_pv_32on64_domain(d) ) return 0; d->arch.is_32bit_pv = d->arch.has_32bit_shinfo = 1; for ( vcpuid = 0; vcpuid < MAX_VIRT_CPUS; vcpuid++ ) { if ( (d->vcpu[vcpuid] != NULL) && (setup_compat_l4(d->vcpu[vcpuid]) != 0) ) goto undo_and_fail; } domain_set_alloc_bitsize(d); return 0; undo_and_fail: d->arch.is_32bit_pv = d->arch.has_3
/**
 * Instead of dealing with react-router's ever-changing APIs,
 * we use a simple url state manager where we only
 *
 * - read the initial URL state on page load
 * - push updates to the URL later on.
 */
import { select, setFilter, setHighlight } from "./ducks/flows"
import { selectTab } from "./ducks/ui/flow"
import { toggleVisibility } from "./ducks/eventLog"

const Query = {
    SEARCH: "s",
    HIGHLIGHT: "h",
    SHOW_EVENTLOG: "e"
};

export function updateStoreFromUrl(store) {
    const [path, query]   = window.location.hash.substr(1).split("?", 2)
    const path_components = path.substr(1).split("/")

    if (path_components[0] === "flows") {
        if (path_components.length == 3) {
            const [flowId, tab] = path_components.slice(1)
            store.dispatch(select(flowId))
            store.dispatch(selectTab(tab))
        }
    }

    if (query) {
        query
            .split("&")
            .forEach((x) => {
                const [key, value] = x.split("=", 2)
                switch (key) {
                    case Query.SEARCH:
                        store.dispatch(setFilter(value))
                        break
                    case Query.HIGHLIGHT:
                        store.dispatch(setHighlight(value))
                        break
                    case Query.SHOW_EVENTLOG:
                        if (!store.getState().eventLog.visible)
                            store.dispatch(toggleVisibility())
                        break
                    default:
                        console.error(`unimplemented query arg: ${x}`)
                }
            })
    }
}

export function updateUrlFromStore(store) {
    const state    = store.getState()
    let query      = {
        [Query.SEARCH]: state.flows.filter,
        [Query.HIGHLIGHT]: state.flows.highlight,
        [Query.SHOW_EVENTLOG]: state.eventLog.visible,
    }
    const queryStr = Object.keys(query)
        .filter(k => query[k])
        .map(k => `${k}=${query[k]}`)
        .join("&")

    let url
    if (state.flows.selected.length > 0) {
        url = `/flows/${state.flows.selected[0]}/${state.ui.flow.tab}`
    } else {
        url = "/flows"
    }

    if (queryStr) {
        url += "?" + queryStr
    }
    let pathname = window.location.pathname
    if(pathname === "blank") {
        pathname = "/" // this happens in tests...
    }
    if (window.location.hash.substr(1) !== url) {
        history.replaceState(undefined, "", `${pathname}#${url}`)
    }
}

export default function initialize(store) {
    updateStoreFromUrl(store)
    store.subscribe(() => updateUrlFromStore(store))
}
S) || !nctxt->gs_base_user ) all_segs_okay &= loadsegment(gs, nctxt->user_regs.gs); } if ( !is_pv_32on64_domain(n->domain) ) { /* This can only be non-zero if selector is NULL. */ if ( nctxt->fs_base ) wrmsr(MSR_FS_BASE, nctxt->fs_base, nctxt->fs_base>>32); /* Most kernels have non-zero GS base, so don't bother testing. */ /* (This is also a serialising instruction, avoiding AMD erratum #88.) */ wrmsr(MSR_SHADOW_GS_BASE, nctxt->gs_base_kernel, nctxt->gs_base_kernel>>32); /* This can only be non-zero if selector is NULL. */ if ( nctxt->gs_base_user ) wrmsr(MSR_GS_BASE, nctxt->gs_base_user, nctxt->gs_base_user>>32); /* If in kernel mode then switch the GS bases around. */ if ( (n->arch.flags & TF_kernel_mode) ) asm volatile ( "swapgs" ); } if ( unlikely(!all_segs_okay) ) { struct cpu_user_regs *regs = guest_cpu_user_regs(); unsigned long *rsp = (n->arch.flags & TF_kernel_mode) ? (unsigned long *)regs->rsp : (unsigned long *)nctxt->kernel_sp; unsigned long cs_and_mask, rflags; if ( is_pv_32on64_domain(n->domain) ) { unsigned int *esp = ring_1(regs) ? (unsigned int *)regs->rsp : (unsigned int *)nctxt->kernel_sp; unsigned int cs_and_mask, eflags; int ret = 0; /* CS longword also contains full evtchn_upcall_mask. */ cs_and_mask = (unsigned short)regs->cs | ((unsigned int)vcpu_info(n, evtchn_upcall_mask) << 16); /* Fold upcall mask into RFLAGS.IF. */ eflags = regs->_eflags & ~X86_EFLAGS_IF; eflags |= !vcpu_info(n, evtchn_upcall_mask) << 9; if ( !ring_1(regs) ) { ret = put_user(regs->ss, esp-1); ret |= put_user(regs->_esp, esp-2); esp -= 2; } if ( ret | put_user(eflags, esp-1) | put_user(cs_and_mask, esp-2) | put_user(regs->_eip, esp-3) | put_user(nctxt->user_regs.gs, esp-4) | put_user(nctxt->user_regs.fs, esp-5) | put_user(nctxt->user_regs.es, esp-6) | put_user(nctxt->user_regs.ds, esp-7) ) { gdprintk(XENLOG_ERR, "Error while creating compat " "failsafe callback frame.\n"); domain_crash(n->domain); } if ( test_bit(_VGCF_failsafe_disables_events, &n->arch.guest_context.flags) ) vcpu_info(n, evtchn_upcall_mask) = 1; regs->entry_vector = TRAP_syscall; regs->_eflags &= 0xFFFCBEFFUL; regs->ss = FLAT_COMPAT_KERNEL_SS; regs->_esp = (unsigned long)(esp-7); regs->cs = FLAT_COMPAT_KERNEL_CS; regs->_eip = nctxt->failsafe_callback_eip; return; } if ( !(n->arch.flags & TF_kernel_mode) ) toggle_guest_mode(n); else regs->cs &= ~3; /* CS longword also contains full evtchn_upcall_mask. */ cs_and_mask = (unsigned long)regs->cs | ((unsigned long)vcpu_info(n, evtchn_upcall_mask) << 32); /* Fold upcall mask into RFLAGS.IF. */ rflags = regs->rflags & ~X86_EFLAGS_IF; rflags |= !vcpu_info(n, evtchn_upcall_mask) << 9; if ( put_user(regs->ss, rsp- 1) | put_user(regs->rsp, rsp- 2) | put_user(rflags, rsp- 3) | put_user(cs_and_mask, rsp- 4) | put_user(regs->rip, rsp- 5) | put_user(nctxt->user_regs.gs, rsp- 6) | put_user(nctxt->user_regs.fs, rsp- 7) | put_user(nctxt->user_regs.es, rsp- 8) | put_user(nctxt->user_regs.ds, rsp- 9) | put_user(regs->r11, rsp-10) | put_user(regs->rcx, rsp-11) ) { gdprintk(XENLOG_ERR, "Error while creating failsafe " "callback frame.\n"); domain_crash(n->domain); } if ( test_bit(_VGCF_failsafe_disables_events, &n->arch.guest_context.flags) ) vcpu_info(n, evtchn_upcall_mask) = 1; regs->entry_vector = TRAP_syscall; regs->rflags &= ~(X86_EFLAGS_AC|X86_EFLAGS_VM|X86_EFLAGS_RF| X86_EFLAGS_NT|X86_EFLAGS_TF); regs->ss = FLAT_KERNEL_SS; regs->rsp = (unsigned long)(rsp-11); regs->cs = FLAT_KERNEL_CS; regs->rip = nctxt->failsafe_callback_eip; } } static void save_segments(struct vcpu *v) { struct vcpu_guest_context *ctxt = &v->arch.guest_context; struct cpu_user_regs *regs = &ctxt->user_regs; unsigned int dirty_segment_mask = 0; regs->ds = read_segment_register(ds); regs->es = read_segment_register(es); regs->fs = read_segment_register(fs); regs->gs = read_segment_register(gs); if ( regs->ds ) dirty_segment_mask |= DIRTY_DS; if ( regs->es ) dirty_segment_mask |= DIRTY_ES; if ( regs->fs || is_pv_32on64_domain(v->domain) ) { dirty_segment_mask |= DIRTY_FS; ctxt->fs_base = 0; /* != 0 selector kills fs_base */ } else if ( ctxt->fs_base ) { dirty_segment_mask |= DIRTY_FS_BASE; } if ( regs->gs || is_pv_32on64_domain(v->domain) ) { dirty_segment_mask |= DIRTY_GS; ctxt->gs_base_user = 0; /* != 0 selector kills gs_base_user */ } else if ( ctxt->gs_base_user ) { dirty_segment_mask |= DIRTY_GS_BASE_USER; } this_cpu(dirty_segment_mask) = dirty_segment_mask; } #define switch_kernel_stack(v) ((void)0) #elif defined(__i386__) #define load_segments(n) ((void)0) #define save_segments(p) ((void)0) static inline void switch_kernel_stack(struct vcpu *v) { struct tss_struct *tss = &init_tss[smp_processor_id()]; tss->esp1 = v->arch.guest_context.kernel_sp; tss->ss1 = v->arch.guest_context.kernel_ss; } #endif /* __i386__ */ static void paravirt_ctxt_switch_from(struct vcpu *v) { save_segments(v); /* * Disable debug breakpoints. We do this aggressively because if we switch * to an HVM guest we may load DR0-DR3 with values that can cause #DE * inside Xen, before we get a chance to reload DR7, and this cannot always * safely be handled. */ if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) ) write_debugreg(7, 0); } static void paravirt_ctxt_switch_to(struct vcpu *v) { unsigned long cr4; set_int80_direct_trap(v); switch_kernel_stack(v); cr4 = pv_guest_cr4_to_real_cr4(v->arch.guest_context.ctrlreg[4]); if ( unlikely(cr4 != read_cr4()) ) write_cr4(cr4); if ( unlikely(v->arch.guest_context.debugreg[7] & DR7_ACTIVE_MASK) ) { write_debugreg(0, v->arch.guest_context.debugreg[0]); write_debugreg(1, v->arch.guest_context.debugreg[1]); write_debugreg(2, v->arch.guest_context.debugreg[2]); write_debugreg(3, v->arch.guest_context.debugreg[3]); write_debugreg(6, v->arch.guest_context.debugreg[6]); write_debugreg(7, v->arch.guest_context.debugreg[7]); } } static void __context_switch(void) { struct cpu_user_regs *stack_regs = guest_cpu_user_regs(); unsigned int i, cpu = smp_processor_id(); struct vcpu *p = per_cpu(curr_vcpu, cpu); struct vcpu *n = current; struct desc_struct *gdt; struct page_info *page; struct desc_ptr gdt_desc; ASSERT(p != n); ASSERT(cpus_empty(n->vcpu_dirty_cpumask)); if ( !is_idle_vcpu(p) ) { memcpy(&p->arch.guest_context.user_regs, stack_regs, CTXT_SWITCH_STACK_BYTES); unlazy_fpu(p); p->arch.ctxt_switch_from(p); } if ( !is_idle_vcpu(n) ) { memcpy(stack_regs, &n->arch.guest_context.user_regs, CTXT_SWITCH_STACK_BYTES); n->arch.ctxt_switch_to(n); } if ( p->domain != n->domain ) cpu_set(cpu, n->domain->domain_dirty_cpumask); cpu_set(cpu, n->vcpu_dirty_cpumask); gdt = !is_pv_32on64_vcpu(n) ? per_cpu(gdt_table, cpu) : per_cpu(compat_gdt_table, cpu); page = virt_to_page(gdt); for (i = 0; i < NR_RESERVED_GDT_PAGES; ++i) { l1e_write(n->domain->arch.mm_perdomain_pt + (n->vcpu_id << GDT_LDT_VCPU_SHIFT) + FIRST_RESERVED_GDT_PAGE + i, l1e_from_page(page + i, __PAGE_HYPERVISOR)); } if ( p->vcpu_id != n->vcpu_id ) { gdt_desc.limit = LAST_RESERVED_GDT_BYTE; gdt_desc.base = (unsigned long)(gdt - FIRST_RESERVED_GDT_ENTRY); asm volatile ( "lgdt %0" : : "m" (gdt_desc) ); } write_ptbase(n); if ( p->vcpu_id != n->vcpu_id ) { gdt_desc.base = GDT_VIRT_START(n); asm volatile ( "lgdt %0" : : "m" (gdt_desc) ); } if ( p->domain != n->domain ) cpu_clear(cpu, p->domain->domain_dirty_cpumask); cpu_clear(cpu, p->vcpu_dirty_cpumask); per_cpu(curr_vcpu, cpu) = n; } void context_switch(struct vcpu *prev, struct vcpu *next) { unsigned int cpu = smp_processor_id(); cpumask_t dirty_mask = next->vcpu_dirty_cpumask; ASSERT(local_irq_is_enabled()); /* Allow at most one CPU at a time to be dirty. */ ASSERT(cpus_weight(dirty_mask) <= 1); if ( unlikely(!cpu_isset(cpu, dirty_mask) && !cpus_empty(dirty_mask)) ) { /* Other cpus call __sync_lazy_execstate from flush ipi handler. */ if ( !cpus_empty(next->vcpu_dirty_cpumask) ) flush_tlb_mask(next->vcpu_dirty_cpumask); } local_irq_disable(); if ( is_hvm_vcpu(prev) && !list_empty(&prev->arch.hvm_vcpu.tm_list) ) pt_save_timer(prev); set_current(next); if ( (per_cpu(curr_vcpu, cpu) == next) || is_idle_vcpu(next) ) { local_irq_enable(); } else { __context_switch(); #ifdef CONFIG_COMPAT if ( !is_hvm_vcpu(next) && (is_idle_vcpu(prev) || is_hvm_vcpu(prev) || is_pv_32on64_vcpu(prev) != is_pv_32on64_vcpu(next)) ) { uint64_t efer = read_efer(); if ( !(efer & EFER_SCE) ) write_efer(efer | EFER_SCE); } #endif /* Re-enable interrupts before restoring state which may fault. */ local_irq_enable(); if ( !is_hvm_vcpu(next) ) { load_LDT(next); load_segments(next); } } context_saved(prev); /* Update per-VCPU guest runstate shared memory area (if registered). */ if ( !guest_handle_is_null(runstate_guest(next)) ) { if ( !is_pv_32on64_domain(next->domain) ) __copy_to_guest(runstate_guest(next), &next->runstate, 1); #ifdef CONFIG_COMPAT else { struct compat_vcpu_runstate_info info; XLAT_vcpu_runstate_info(&info, &next->runstate); __copy_to_guest(next->runstate_guest.compat, &info, 1); } #endif } schedule_tail(next); BUG(); } void continue_running(struct vcpu *same) { schedule_tail(same); BUG(); } int __sync_lazy_execstate(void) { unsigned long flags; int switch_required; local_irq_save(flags); switch_required = (this_cpu(curr_vcpu) != current); if ( switch_required ) { ASSERT(current == idle_vcpu[smp_processor_id()]); __context_switch(); } local_irq_restore(flags); return switch_required; } void sync_vcpu_execstate(struct vcpu *v) { if ( cpu_isset(smp_processor_id(), v->vcpu_dirty_cpumask) ) (void)__sync_lazy_execstate(); /* Other cpus call __sync_lazy_execstate from flush ipi handler. */ flush_tlb_mask(v->vcpu_dirty_cpumask); } struct migrate_info { long (*func)(void *data); void *data; void (*saved_schedule_tail)(struct vcpu *); cpumask_t saved_affinity; unsigned int nest; }; static void continue_hypercall_on_cpu_helper(struct vcpu *v) { struct cpu_user_regs *regs = guest_cpu_user_regs(); struct migrate_info *info = v->arch.continue_info; cpumask_t mask = info->saved_affinity; void (*saved_schedule_tail)(struct vcpu *) = info->saved_schedule_tail; regs->eax = info->func(info->data); if ( info->nest-- == 0 ) { xfree(info); v->arch.schedule_tail = saved_schedule_tail; v->arch.continue_info = NULL; vcpu_unlock_affinity(v, &mask); } (*saved_schedule_tail)(v); } int continue_hypercall_on_cpu(int cpu, long (*func)(void *data), void *data) { struct vcpu *v = current; struct migrate_info *info; cpumask_t mask = cpumask_of_cpu(cpu); int rc; if ( cpu == smp_processor_id() ) return func(data); info = v->arch.continue_info; if ( info == NULL ) { info = xmalloc(struct migrate_info); if ( info == NULL ) return -ENOMEM; rc = vcpu_lock_affinity(v, &mask); if ( rc ) { xfree(info); return rc; } info->saved_schedule_tail = v->arch.schedule_tail; info->saved_affinity = mask; info->nest = 0; v->arch.schedule_tail = continue_hypercall_on_cpu_helper; v->arch.continue_info = info; } else { BUG_ON(info->nest != 0); rc = vcpu_locked_change_affinity(v, &mask); if ( rc ) return rc; info->nest++; } info->func = func; info->data = data; /* Dummy return value will be overwritten by new schedule_tail. */ BUG_ON(!test_bit(SCHEDULE_SOFTIRQ, &softirq_pending(smp_processor_id()))); return 0; } #define next_arg(fmt, args) ({ \ unsigned long __arg; \ switch ( *(fmt)++ ) \ { \ case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ default: __arg = 0; BUG(); \ } \ __arg; \ }) DEFINE_PER_CPU(char, hc_preempted); unsigned long hypercall_create_continuation( unsigned int op, const char *format, ...) { struct mc_state *mcs = &this_cpu(mc_state); struct cpu_user_regs *regs; const char *p = format; unsigned long arg; unsigned int i; va_list args; va_start(args, format); if ( test_bit(_MCSF_in_multicall, &mcs->flags) ) { __set_bit(_MCSF_call_preempted, &mcs->flags); for ( i = 0; *p != '\0'; i++ ) mcs->call.args[i] = next_arg(p, args); if ( is_pv_32on64_domain(current->domain) ) { for ( ; i < 6; i++ ) mcs->call.args[i] = 0; } } else { regs = guest_cpu_user_regs(); regs->eax = op; /* * For PV guest, we update EIP to re-execute 'syscall' / 'int 0x82'; * HVM does not need this since 'vmcall' / 'vmmcall' is fault-like. */ if ( !is_hvm_vcpu(current) ) regs->eip -= 2; /* re-execute 'syscall' / 'int 0x82' */ #ifdef __x86_64__ if ( !is_hvm_vcpu(current) ? !is_pv_32on64_vcpu(current) : (hvm_guest_x86_mode(current) == 8) ) { for ( i = 0; *p != '\0'; i++ ) { arg = next_arg(p, args); switch ( i ) { case 0: regs->rdi = arg; break; case 1: regs->rsi = arg; break; case 2: regs->rdx = arg; break; case 3: regs->r10 = arg; break; case 4: regs->r8 = arg; break; case 5: regs->r9 = arg; break; } } } else #endif { if ( supervisor_mode_kernel ) regs->eip &= ~31; /* re-execute entire hypercall entry stub */ for ( i = 0; *p != '\0'; i++ ) { arg = next_arg(p, args); switch ( i ) { case 0: regs->ebx = arg; break; case 1: regs->ecx = arg; break; case 2: regs->edx = arg; break; case 3: regs->esi = arg; break; case 4: regs->edi = arg; break; case 5: regs->ebp = arg; break; } } } this_cpu(hc_preempted) = 1; } va_end(args); return op; } #ifdef CONFIG_COMPAT int hypercall_xlat_continuation(unsigned int *id, unsigned int mask, ...) { int rc = 0; struct mc_state *mcs = &this_cpu(mc_state); struct cpu_user_regs *regs; unsigned int i, cval = 0; unsigned long nval = 0; va_list args; BUG_ON(*id > 5); BUG_ON(mask & (1U << *id)); va_start(args, mask); if ( test_bit(_MCSF_in_multicall, &mcs->flags) ) { if ( !test_bit(_MCSF_call_preempted, &mcs->flags) ) return 0; for ( i = 0; i < 6; ++i, mask >>= 1 ) { if ( mask & 1 ) { nval = va_arg(args, unsigned long); cval = va_arg(args, unsigned int); if ( cval == nval ) mask &= ~1U; else BUG_ON(nval == (unsigned int)nval); } else if ( id && *id == i ) { *id = mcs->call.args[i]; id = NULL; } if ( (mask & 1) && mcs->call.args[i] == nval ) { mcs->call.args[i] = cval; ++rc; } else BUG_ON(mcs->call.args[i] != (unsigned int)mcs->call.args[i]); } } else { regs = guest_cpu_user_regs(); for ( i = 0; i < 6; ++i, mask >>= 1 ) { unsigned long *reg; switch ( i ) { case 0: reg = ®s->ebx; break; case 1: reg = ®s->ecx; break; case 2: reg = ®s->edx; break; case 3: reg = ®s->esi; break; case 4: reg = ®s->edi; break; case 5: reg = ®s->ebp; break; default: BUG(); reg = NULL; break; } if ( (mask & 1) ) { nval = va_arg(args, unsigned long); cval = va_arg(args, unsigned int); if ( cval == nval ) mask &= ~1U; else BUG_ON(nval == (unsigned int)nval); } else if ( id && *id == i ) { *id = *reg; id = NULL; } if ( (mask & 1) && *reg == nval ) { *reg = cval; ++rc; } else BUG_ON(*reg != (unsigned int)*reg); } } va_end(args); return rc; } #endif static int relinquish_memory( struct domain *d, struct list_head *list, unsigned long type) { struct list_head *ent; struct page_info *page; unsigned long x, y; int ret = 0; /* Use a recursive lock, as we may enter 'free_domheap_page'. */ spin_lock_recursive(&d->page_alloc_lock); ent = list->next; while ( ent != list ) { page = list_entry(ent, struct page_info, list); /* Grab a reference to the page so it won't disappear from under us. */ if ( unlikely(!get_page(page, d)) ) { /* Couldn't get a reference -- someone is freeing this page. */ ent = ent->next; list_move_tail(&page->list, &d->arch.relmem_list); continue; } if ( test_and_clear_bit(_PGT_pinned, &page->u.inuse.type_info) ) put_page_and_type(page); if ( test_and_clear_bit(_PGC_allocated, &page->count_info) ) put_page(page); #ifdef DOMAIN_DESTRUCT_AVOID_RECURSION /* * Forcibly drop reference counts of page tables above top most (which * were skipped to prevent long latencies due to deep recursion - see * the special treatment in free_lX_table()). */ y = page->u.inuse.type_info; if ( (type < PGT_root_page_table) && unlikely(((y + PGT_type_mask) & (PGT_type_mask|PGT_validated)) == type) ) { BUG_ON((y & PGT_count_mask) >= (page->count_info & PGC_count_mask)); while ( y & PGT_count_mask ) { put_page_and_type(page); y = page->u.inuse.type_info; } } #endif /* * Forcibly invalidate top-most, still valid page tables at this point * to break circular 'linear page table' references as well as clean up * partially validated pages. This is okay because MMU structures are * not shared across domains and this domain is now dead. Thus top-most * valid tables are not in use so a non-zero count means circular * reference or partially validated. */ y = page->u.inuse.type_info; for ( ; ; ) { x = y; if ( likely((x & PGT_type_mask) != type) || likely(!(x & (PGT_validated|PGT_partial))) ) break; y = cmpxchg(&page->u.inuse.type_info, x, x & ~(PGT_validated|PGT_partial)); if ( likely(y == x) ) { if ( free_page_type(page, x, 0) != 0 ) BUG(); break; } } /* Follow the list chain and /then/ potentially free the page. */ ent = ent->next; list_move_tail(&page->list, &d->arch.relmem_list); put_page(page); if ( hypercall_preempt_check() ) { ret = -EAGAIN; goto out; } } list_splice_init(&d->arch.relmem_list, list); out: spin_unlock_recursive(&d->page_alloc_lock); return ret; } static void vcpu_destroy_pagetables(struct vcpu *v) { struct domain *d = v->domain; unsigned long pfn; #ifdef __x86_64__ if ( is_pv_32on64_vcpu(v) ) { pfn = l4e_get_pfn(*(l4_pgentry_t *) __va(pagetable_get_paddr(v->arch.guest_table))); if ( pfn != 0 ) { if ( paging_mode_refcounts(d) ) put_page(mfn_to_page(pfn)); else put_page_and_type(mfn_to_page(pfn)); } l4e_write( (l4_pgentry_t *)__va(pagetable_get_paddr(v->arch.guest_table)), l4e_empty()); v->arch.cr3 = 0; return; } #endif pfn = pagetable_get_pfn(v->arch.guest_table); if ( pfn != 0 ) { if ( paging_mode_refcounts(d) ) put_page(mfn_to_page(pfn)); else put_page_and_type(mfn_to_page(pfn)); v->arch.guest_table = pagetable_null(); } #ifdef __x86_64__ /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */ pfn = pagetable_get_pfn(v->arch.guest_table_user); if ( pfn != 0 ) { if ( !is_pv_32bit_vcpu(v) ) { if ( paging_mode_refcounts(d) ) put_page(mfn_to_page(pfn)); else put_page_and_type(mfn_to_page(pfn)); } v->arch.guest_table_user = pagetable_null(); } #endif v->arch.cr3 = 0; } int domain_relinquish_resources(struct domain *d) { int ret; struct vcpu *v; BUG_ON(!cpus_empty(d->domain_dirty_cpumask)); switch ( d->arch.relmem ) { case RELMEM_not_started: /* Tear down paging-assistance stuff. */ paging_teardown(d); for_each_vcpu ( d, v ) { /* Drop the in-use references to page-table bases. */ vcpu_destroy_pagetables(v); /* * Relinquish GDT mappings. No need for explicit unmapping of the * LDT as it automatically gets squashed with the guest mappings. */ destroy_gdt(v); unmap_vcpu_info(v); } d->arch.relmem = RELMEM_xen; /* fallthrough */ /* Relinquish every page of memory. */ case RELMEM_xen: ret = relinquish_memory(d, &d->xenpage_list, ~0UL); if ( ret ) return ret; #if CONFIG_PAGING_LEVELS >= 4 d->arch.relmem = RELMEM_l4; /* fallthrough */ case RELMEM_l4: ret = relinquish_memory(d, &d->page_list, PGT_l4_page_table); if ( ret ) return ret; #endif #if CONFIG_PAGING_LEVELS >= 3 d->arch.relmem = RELMEM_l3; /* fallthrough */ case RELMEM_l3: ret = relinquish_memory(d, &d->page_list, PGT_l3_page_table); if ( ret ) return ret; #endif d->arch.relmem = RELMEM_l2; /* fallthrough */ case RELMEM_l2: ret = relinquish_memory(d, &d->page_list, PGT_l2_page_table); if ( ret ) return ret; d->arch.relmem = RELMEM_done; /* fallthrough */ case RELMEM_done: #ifdef DOMAIN_DESTRUCT_AVOID_RECURSION ret = relinquish_memory(d, &d->page_list, PGT_l1_page_table); if ( ret ) return ret; #endif break; default: BUG(); } /* Free page used by xen oprofile buffer. */ free_xenoprof_pages(d); if ( is_hvm_domain(d) ) hvm_domain_relinquish_resources(d); return 0; } void arch_dump_domain_info(struct domain *d) { paging_dump_domain_info(d); } void arch_dump_vcpu_info(struct vcpu *v) { paging_dump_vcpu_info(v); } void domain_cpuid( struct domain *d, unsigned int input, unsigned int sub_input, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { cpuid_input_t *cpuid; int i; for ( i = 0; i < MAX_CPUID_INPUT; i++ ) { cpuid = &d->arch.cpuids[i]; if ( (cpuid->input[0] == input) && ((cpuid->input[1] == XEN_CPUID_INPUT_UNUSED) || (cpuid->input[1] == sub_input)) ) { *eax = cpuid->eax; *ebx = cpuid->ebx; *ecx = cpuid->ecx; *edx = cpuid->edx; return; } } *eax = *ebx = *ecx = *edx = 0; } /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */