/* * Xen SMP booting functions * * See arch/i386/kernel/smpboot.c for copyright and credits for derived * portions of this file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern irqreturn_t smp_reschedule_interrupt(int, void *, struct pt_regs *); extern irqreturn_t smp_call_function_interrupt(int, void *, struct pt_regs *); extern void local_setup_timer(unsigned int cpu); extern void local_teardown_timer(unsigned int cpu); extern void hypervisor_callback(void); extern void failsafe_callback(void); extern void system_call(void); extern void smp_trap_init(trap_info_t *); /* Number of siblings per CPU package */ int smp_num_siblings = 1; int phys_proc_id[NR_CPUS]; /* Package ID of each logical CPU */ EXPORT_SYMBOL(phys_proc_id); int cpu_core_id[NR_CPUS]; /* Core ID of each logical CPU */ EXPORT_SYMBOL(cpu_core_id); #if defined(__i386__) int cpu_llc_id[NR_CPUS] __cpuinitdata = {[0 ... NR_CPUS-1] = BAD_APICID}; #elif defined(__x86_64__) u8 cpu_llc_id[NR_CPUS] __cpuinitdata = {[0 ... NR_CPUS-1] = BAD_APICID}; #endif cpumask_t cpu_online_map; EXPORT_SYMBOL(cpu_online_map); cpumask_t cpu_possible_map; EXPORT_SYMBOL(cpu_possible_map); struct cpuinfo_x86 cpu_data[NR_CPUS] __cacheline_aligned; EXPORT_SYMBOL(cpu_data); #ifdef CONFIG_HOTPLUG_CPU DEFINE_PER_CPU(int, cpu_state) = { 0 }; #endif static DEFINE_PER_CPU(int, resched_irq); static DEFINE_PER_CPU(int, callfunc_irq); static char resched_name[NR_CPUS][15]; static char callfunc_name[NR_CPUS][15]; u8 cpu_2_logical_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID }; void *xquad_portio; cpumask_t cpu_sibling_map[NR_CPUS] __cacheline_aligned; cpumask_t cpu_core_map[NR_CPUS] __cacheline_aligned; EXPORT_SYMBOL(cpu_core_map); #if defined(__i386__) u8 x86_cpu_to_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = 0xff }; EXPORT_SYMBOL(x86_cpu_to_apicid); #elif !defined(CONFIG_X86_IO_APIC) unsigned int maxcpus = NR_CPUS; #endif void __init prefill_possible_map(void) { int i, rc; for (i = 0; i < NR_CPUS; i++) { rc = HYPERVISOR_vcpu_op(VCPUOP_is_up, i, NULL); if (rc >= 0) cpu_set(i, cpu_possible_map); } } void __init smp_alloc_memory(void) { } static inline void set_cpu_sibling_map(int cpu) { phys_proc_id[cpu] = cpu; cpu_core_id[cpu] = 0; cpu_sibling_map[cpu] = cpumask_of_cpu(cpu); cpu_core_map[cpu] = cpumask_of_cpu(cpu); cpu_data[cpu].booted_cores = 1; } static void xen_smp_intr_init(unsigned int cpu) { sprintf(resched_name[cpu], "resched%d", cpu); per_cpu(resched_irq, cpu) = bind_ipi_to_irqhandler( RESCHEDULE_VECTOR, cpu, smp_reschedule_interrupt, SA_INTERRUPT, resched_name[cpu], NULL); BUG_ON(per_cpu(resched_irq, cpu) < 0); sprintf(callfunc_name[cpu], "callfunc%d", cpu); per_cpu(callfunc_irq, cpu) = bind_ipi_to_irqhandler( CALL_FUNCTION_VECTOR, cpu, smp_call_function_interrupt, SA_INTERRUPT, callfunc_name[cpu], NULL); BUG_ON(per_cpu(callfunc_irq, cpu) < 0); if (cpu != 0) local_setup_timer(cpu); } #ifdef CONFIG_HOTPLUG_CPU static void xen_smp_intr_exit(unsigned int cpu) { if (cpu != 0) local_teardown_timer(cpu); unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); } #endif void cpu_bringup(void) { cpu_init(); touch_softlockup_watchdog(); preempt_disable(); local_irq_enable(); } static void cpu_bringup_and_idle(void) { cpu_bringup(); cpu_idle(); } void cpu_initialize_context(unsigned int cpu) { vcpu_guest_context_t ctxt; struct task_struct *idle = idle_task(cpu); #ifdef __x86_64__ struct desc_ptr *gdt_descr = &cpu_gdt_descr[cpu]; #else struct Xgt_desc_struct *gdt_descr = &per_cpu(cpu_gdt_descr, cpu); #endif if (cpu == 0) return; memset(&ctxt, 0, sizeof(ctxt)); ctxt.flags = VGCF_IN_KERNEL; ctxt.user_regs.ds = __USER_DS; ctxt.user_regs.es = __USER_DS; ctxt.user_regs.fs = 0; ctxt.user_regs.gs = 0; ctxt.user_regs.ss = __KERNEL_DS; ctxt.user_regs.eip = (unsigned long)cpu_bringup_and_idle; ctxt.user_regs.eflags = X86_EFLAGS_IF | 0x1000; /* IOPL_RING1 */ memset(&ctxt.fpu_ctxt, 0, sizeof(ctxt.fpu_ctxt)); smp_trap_init(ctxt.trap_ctxt); ctxt.ldt_ents = 0; ctxt.gdt_frames[0] = virt_to_mfn(gdt_descr->address); ctxt.gdt_ents = gdt_descr->size / 8; #ifdef __i386__ ctxt.user_regs.cs = __KERNEL_CS; ctxt.user_regs.esp = idle->thread.esp0 - sizeof(struct pt_regs); ctxt.kernel_ss = __KERNEL_DS; ctxt.kernel_sp = idle->thread.esp0; ctxt.event_callback_cs = __KERNEL_CS; ctxt.event_callback_eip = (unsigned long)hypervisor_callback; ctxt.failsafe_callback_cs = __KERNEL_CS; ctxt.failsafe_callback_eip = (unsigned long)failsafe_callback; ctxt.ctrlreg[3] = xen_pfn_to_cr3(virt_to_mfn(swapper_pg_dir)); #else /* __x86_64__ */ ctxt.user_regs.cs = __KERNEL_CS; ctxt.user_regs.esp = idle->thread.rsp0 - sizeof(struct pt_regs); ctxt.kernel_ss = __KERNEL_DS; ctxt.kernel_sp = idle->thread.rsp0; ctxt.event_callback_eip = (unsigned long)hypervisor_callback; ctxt.failsafe_callback_eip = (unsigned long)failsafe_callback; ctxt.syscall_callback_eip = (unsigned long)system_call; ctxt.ctrlreg[3] = xen_pfn_to_cr3(virt_to_mfn(init_level4_pgt)); ctxt.gs_base_kernel = (unsigned long)(cpu_pda(cpu)); #endif BUG_ON(HYPERVISOR_vcpu_op(VCPUOP_initialise, cpu, &ctxt)); } void __init smp_prepare_cpus(unsigned int max_cpus) { int cpu; struct task_struct *idle; #ifdef __x86_64__ struct desc_ptr *gdt_descr; #else struct Xgt_desc_struct *gdt_descr; #endif boot_cpu_data.apicid = 0; cpu_data[0] = boot_cpu_data; cpu_2_logical_apicid[0] = 0; x86_cpu_to_apicid[0] = 0; current_thread_info()->cpu = 0; for (cpu = 0; cpu < NR_CPUS; cpu++) { cpus_clear(cpu_sibling_map[cpu]); cpus_clear(cpu_core_map[cpu]); } set_cpu_sibling_map(0); xen_smp_intr_init(0); for_each_cpu_mask (cpu, cpu_possible_map) { if (cpu == 0) continue; #ifdef __x86_64__ gdt_descr = &cpu_gdt_descr[cpu]; #else gdt_descr = &per_cpu(cpu_gdt_descr, cpu); #endif gdt_descr->address = get_zeroed_page(GFP_KERNEL); if (unlikely(!gdt_descr->address)) { printk(KERN_CRIT "CPU%d failed to allocate GDT\n", cpu); continue; } gdt_descr->size = GDT_SIZE; memcpy((void *)gdt_descr->address, cpu_gdt_table, GDT_SIZE); make_page_readonly( (void *)gdt_descr->address, XENFEAT_writable_descriptor_tables); cpu_data[cpu] = boot_cpu_data; cpu_data[cpu].apicid = cpu; cpu_2_logical_apicid[cpu] = cpu; x86_cpu_to_apicid[cpu] = cpu; idle = fork_idle(cpu); if (IS_ERR(idle)) panic("failed fork for CPU %d", cpu); #ifdef __x86_64__ cpu_pda(cpu)->pcurrent = idle; cpu_pda(cpu)->cpunumber = cpu; clear_ti_thread_flag(idle->thread_info, TIF_FORK); #endif irq_ctx_init(cpu); #ifdef CONFIG_HOTPLUG_CPU if (xen_start_info->flags & SIF_INITDOMAIN) cpu_set(cpu, cpu_present_map); #else cpu_set(cpu, cpu_present_map); #endif cpu_initialize_context(cpu); } init_xenbus_allowed_cpumask(); /* Currently, Xen gives no dynamic NUMA/HT info. */ for (cpu = 1; cpu < NR_CPUS; cpu++) { cpu_sibling_map[cpu] = cpumask_of_cpu(cpu); cpu_core_map[cpu] = cpumask_of_cpu(cpu); } #ifdef CONFIG_X86_IO_APIC /* * Here we can be sure that there is an IO-APIC in the system. Let's * go and set it up: */ if (!skip_ioapic_setup && nr_ioapics) setup_IO_APIC(); #endif } void __devinit smp_prepare_boot_cpu(void) { } #ifdef CONFIG_HOTPLUG_CPU /* * Initialize cpu_present_map late to skip SMP boot code in init/main.c. * But do it early enough to catch critical for_each_present_cpu() loops * in i386-specific code. */ static int __init initialize_cpu_present_map(void) { cpu_present_map = cpu_possible_map; return 0; } core_initcall(initialize_cpu_present_map); static void remove_siblinginfo(int cpu) { phys_proc_id[cpu] = BAD_APICID; cpu_core_id[cpu] = BAD_APICID; cpus_clear(cpu_sibling_map[cpu]); cpus_clear(cpu_core_map[cpu]); cpu_data[cpu].booted_cores = 0; } int __cpu_disable(void) { cpumask_t map = cpu_online_map; int cpu = smp_processor_id(); if (cpu == 0) return -EBUSY; remove_siblinginfo(cpu); cpu_clear(cpu, map); fixup_irqs(map); cpu_clear(cpu, cpu_online_map); return 0; } void __cpu_die(unsigned int cpu) { while (HYPERVISOR_vcpu_op(VCPUOP_is_up, cpu, NULL)) { current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/10); } xen_smp_intr_exit(cpu); #ifdef __i386__ if (num_online_cpus() == 1) alternatives_smp_switch(0); #endif } #else /* !CONFIG_HOTPLUG_CPU */ int __cpu_disable(void) { return -ENOSYS; } void __cpu_die(unsigned int cpu) { BUG(); } #endif /* CONFIG_HOTPLUG_CPU */ int __devinit __cpu_up(unsigned int cpu) { int rc; rc = cpu_up_check(cpu); if (rc) return rc; #ifdef __i386__ if (num_online_cpus() == 1) alternatives_smp_switch(1); #endif /* This must be done before setting cpu_online_map */ set_cpu_sibling_map(cpu); wmb(); xen_smp_intr_init(cpu); cpu_set(cpu, cpu_online_map); rc = HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); BUG_ON(rc); return 0; } void __init smp_cpus_done(unsigned int max_cpus) { } #ifndef CONFIG_X86_LOCAL_APIC int setup_profiling_timer(unsigned int multiplier) { return -EINVAL; } #endif