Vitaly Kuznetsov | 98f2a47 | 2017-03-14 18:35:40 +0100 | [diff] [blame^] | 1 | #include <linux/cpu.h> |
| 2 | #include <linux/kexec.h> |
| 3 | |
| 4 | #include <xen/features.h> |
| 5 | #include <xen/events.h> |
| 6 | #include <xen/interface/memory.h> |
| 7 | |
| 8 | #include <asm/cpu.h> |
| 9 | #include <asm/smp.h> |
| 10 | #include <asm/reboot.h> |
| 11 | #include <asm/setup.h> |
| 12 | #include <asm/hypervisor.h> |
| 13 | |
| 14 | #include <asm/xen/cpuid.h> |
| 15 | #include <asm/xen/hypervisor.h> |
| 16 | |
| 17 | #include "xen-ops.h" |
| 18 | #include "mmu.h" |
| 19 | #include "smp.h" |
| 20 | |
| 21 | void __ref xen_hvm_init_shared_info(void) |
| 22 | { |
| 23 | int cpu; |
| 24 | struct xen_add_to_physmap xatp; |
| 25 | static struct shared_info *shared_info_page; |
| 26 | |
| 27 | if (!shared_info_page) |
| 28 | shared_info_page = (struct shared_info *) |
| 29 | extend_brk(PAGE_SIZE, PAGE_SIZE); |
| 30 | xatp.domid = DOMID_SELF; |
| 31 | xatp.idx = 0; |
| 32 | xatp.space = XENMAPSPACE_shared_info; |
| 33 | xatp.gpfn = __pa(shared_info_page) >> PAGE_SHIFT; |
| 34 | if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) |
| 35 | BUG(); |
| 36 | |
| 37 | HYPERVISOR_shared_info = (struct shared_info *)shared_info_page; |
| 38 | |
| 39 | /* xen_vcpu is a pointer to the vcpu_info struct in the shared_info |
| 40 | * page, we use it in the event channel upcall and in some pvclock |
| 41 | * related functions. We don't need the vcpu_info placement |
| 42 | * optimizations because we don't use any pv_mmu or pv_irq op on |
| 43 | * HVM. |
| 44 | * When xen_hvm_init_shared_info is run at boot time only vcpu 0 is |
| 45 | * online but xen_hvm_init_shared_info is run at resume time too and |
| 46 | * in that case multiple vcpus might be online. */ |
| 47 | for_each_online_cpu(cpu) { |
| 48 | /* Leave it to be NULL. */ |
| 49 | if (xen_vcpu_nr(cpu) >= MAX_VIRT_CPUS) |
| 50 | continue; |
| 51 | per_cpu(xen_vcpu, cpu) = |
| 52 | &HYPERVISOR_shared_info->vcpu_info[xen_vcpu_nr(cpu)]; |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | static void __init init_hvm_pv_info(void) |
| 57 | { |
| 58 | int major, minor; |
| 59 | uint32_t eax, ebx, ecx, edx, base; |
| 60 | |
| 61 | base = xen_cpuid_base(); |
| 62 | eax = cpuid_eax(base + 1); |
| 63 | |
| 64 | major = eax >> 16; |
| 65 | minor = eax & 0xffff; |
| 66 | printk(KERN_INFO "Xen version %d.%d.\n", major, minor); |
| 67 | |
| 68 | xen_domain_type = XEN_HVM_DOMAIN; |
| 69 | |
| 70 | /* PVH set up hypercall page in xen_prepare_pvh(). */ |
| 71 | if (xen_pvh_domain()) |
| 72 | pv_info.name = "Xen PVH"; |
| 73 | else { |
| 74 | u64 pfn; |
| 75 | uint32_t msr; |
| 76 | |
| 77 | pv_info.name = "Xen HVM"; |
| 78 | msr = cpuid_ebx(base + 2); |
| 79 | pfn = __pa(hypercall_page); |
| 80 | wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32)); |
| 81 | } |
| 82 | |
| 83 | xen_setup_features(); |
| 84 | |
| 85 | cpuid(base + 4, &eax, &ebx, &ecx, &edx); |
| 86 | if (eax & XEN_HVM_CPUID_VCPU_ID_PRESENT) |
| 87 | this_cpu_write(xen_vcpu_id, ebx); |
| 88 | else |
| 89 | this_cpu_write(xen_vcpu_id, smp_processor_id()); |
| 90 | } |
| 91 | |
| 92 | #ifdef CONFIG_KEXEC_CORE |
| 93 | static void xen_hvm_shutdown(void) |
| 94 | { |
| 95 | native_machine_shutdown(); |
| 96 | if (kexec_in_progress) |
| 97 | xen_reboot(SHUTDOWN_soft_reset); |
| 98 | } |
| 99 | |
| 100 | static void xen_hvm_crash_shutdown(struct pt_regs *regs) |
| 101 | { |
| 102 | native_machine_crash_shutdown(regs); |
| 103 | xen_reboot(SHUTDOWN_soft_reset); |
| 104 | } |
| 105 | #endif |
| 106 | |
| 107 | static int xen_cpu_up_prepare_hvm(unsigned int cpu) |
| 108 | { |
| 109 | int rc; |
| 110 | |
| 111 | /* |
| 112 | * This can happen if CPU was offlined earlier and |
| 113 | * offlining timed out in common_cpu_die(). |
| 114 | */ |
| 115 | if (cpu_report_state(cpu) == CPU_DEAD_FROZEN) { |
| 116 | xen_smp_intr_free(cpu); |
| 117 | xen_uninit_lock_cpu(cpu); |
| 118 | } |
| 119 | |
| 120 | if (cpu_acpi_id(cpu) != U32_MAX) |
| 121 | per_cpu(xen_vcpu_id, cpu) = cpu_acpi_id(cpu); |
| 122 | else |
| 123 | per_cpu(xen_vcpu_id, cpu) = cpu; |
| 124 | xen_vcpu_setup(cpu); |
| 125 | |
| 126 | if (xen_feature(XENFEAT_hvm_safe_pvclock)) |
| 127 | xen_setup_timer(cpu); |
| 128 | |
| 129 | rc = xen_smp_intr_init(cpu); |
| 130 | if (rc) { |
| 131 | WARN(1, "xen_smp_intr_init() for CPU %d failed: %d\n", |
| 132 | cpu, rc); |
| 133 | return rc; |
| 134 | } |
| 135 | return 0; |
| 136 | } |
| 137 | |
| 138 | static int xen_cpu_dead_hvm(unsigned int cpu) |
| 139 | { |
| 140 | xen_smp_intr_free(cpu); |
| 141 | |
| 142 | if (xen_feature(XENFEAT_hvm_safe_pvclock)) |
| 143 | xen_teardown_timer(cpu); |
| 144 | |
| 145 | return 0; |
| 146 | } |
| 147 | |
| 148 | static void __init xen_hvm_guest_init(void) |
| 149 | { |
| 150 | if (xen_pv_domain()) |
| 151 | return; |
| 152 | |
| 153 | init_hvm_pv_info(); |
| 154 | |
| 155 | xen_hvm_init_shared_info(); |
| 156 | |
| 157 | xen_panic_handler_init(); |
| 158 | |
| 159 | BUG_ON(!xen_feature(XENFEAT_hvm_callback_vector)); |
| 160 | |
| 161 | xen_hvm_smp_init(); |
| 162 | WARN_ON(xen_cpuhp_setup(xen_cpu_up_prepare_hvm, xen_cpu_dead_hvm)); |
| 163 | xen_unplug_emulated_devices(); |
| 164 | x86_init.irqs.intr_init = xen_init_IRQ; |
| 165 | xen_hvm_init_time_ops(); |
| 166 | xen_hvm_init_mmu_ops(); |
| 167 | |
| 168 | if (xen_pvh_domain()) |
| 169 | machine_ops.emergency_restart = xen_emergency_restart; |
| 170 | #ifdef CONFIG_KEXEC_CORE |
| 171 | machine_ops.shutdown = xen_hvm_shutdown; |
| 172 | machine_ops.crash_shutdown = xen_hvm_crash_shutdown; |
| 173 | #endif |
| 174 | } |
| 175 | |
| 176 | static bool xen_nopv; |
| 177 | static __init int xen_parse_nopv(char *arg) |
| 178 | { |
| 179 | xen_nopv = true; |
| 180 | return 0; |
| 181 | } |
| 182 | early_param("xen_nopv", xen_parse_nopv); |
| 183 | |
| 184 | bool xen_hvm_need_lapic(void) |
| 185 | { |
| 186 | if (xen_nopv) |
| 187 | return false; |
| 188 | if (xen_pv_domain()) |
| 189 | return false; |
| 190 | if (!xen_hvm_domain()) |
| 191 | return false; |
| 192 | if (xen_feature(XENFEAT_hvm_pirqs)) |
| 193 | return false; |
| 194 | return true; |
| 195 | } |
| 196 | EXPORT_SYMBOL_GPL(xen_hvm_need_lapic); |
| 197 | |
| 198 | static uint32_t __init xen_platform_hvm(void) |
| 199 | { |
| 200 | if (xen_pv_domain() || xen_nopv) |
| 201 | return 0; |
| 202 | |
| 203 | return xen_cpuid_base(); |
| 204 | } |
| 205 | |
| 206 | const struct hypervisor_x86 x86_hyper_xen_hvm = { |
| 207 | .name = "Xen HVM", |
| 208 | .detect = xen_platform_hvm, |
| 209 | .init_platform = xen_hvm_guest_init, |
| 210 | .pin_vcpu = xen_pin_vcpu, |
| 211 | .x2apic_available = xen_x2apic_para_available, |
| 212 | }; |
| 213 | EXPORT_SYMBOL(x86_hyper_xen_hvm); |