| 1 | /* |
| 2 | * QEMU x86 CPU <-> APIC |
| 3 | * |
| 4 | * Copyright (c) 2003-2004 Fabrice Bellard |
| 5 | * |
| 6 | * SPDX-License-Identifier: MIT |
| 7 | */ |
| 8 | |
| 9 | #include "qemu/osdep.h" |
| 10 | #include "qobject/qdict.h" |
| 11 | #include "qapi/error.h" |
| 12 | #include "monitor/monitor.h" |
| 13 | #include "monitor/hmp.h" |
| 14 | #include "system/hw_accel.h" |
| 15 | #include "system/kvm.h" |
| 16 | #include "system/xen.h" |
| 17 | #include "system/mshv.h" |
| 18 | #include "system/address-spaces.h" |
| 19 | #include "hw/core/qdev-properties.h" |
| 20 | #include "hw/i386/apic_internal.h" |
| 21 | #include "cpu-internal.h" |
| 22 | |
| 23 | APICCommonClass *apic_get_class(Error **errp) |
| 24 | { |
| 25 | const char *apic_type = "apic"; |
| 26 | |
| 27 | /* TODO: in-kernel irqchip for hvf */ |
| 28 | if (kvm_enabled()) { |
| 29 | if (!kvm_irqchip_in_kernel()) { |
| 30 | error_setg(errp, "KVM does not support userspace APIC"); |
| 31 | return NULL; |
| 32 | } |
| 33 | apic_type = "kvm-apic"; |
| 34 | } else if (xen_enabled()) { |
| 35 | apic_type = "xen-apic"; |
| 36 | } else if (whpx_irqchip_in_kernel()) { |
| 37 | apic_type = "whpx-apic"; |
| 38 | } else if (mshv_enabled()) { |
| 39 | apic_type = "mshv-apic"; |
| 40 | } |
| 41 | |
| 42 | return APIC_COMMON_CLASS(object_class_by_name(apic_type)); |
| 43 | } |
| 44 | |
| 45 | void x86_cpu_apic_create(X86CPU *cpu, Error **errp) |
| 46 | { |
| 47 | APICCommonClass *apic_class = apic_get_class(errp); |
| 48 | |
| 49 | if (!apic_class) { |
| 50 | return; |
| 51 | } |
| 52 | |
| 53 | cpu->apic_state = APIC_COMMON(object_new_with_class(OBJECT_CLASS(apic_class))); |
| 54 | object_property_add_child(OBJECT(cpu), "lapic", |
| 55 | OBJECT(cpu->apic_state)); |
| 56 | object_unref(OBJECT(cpu->apic_state)); |
| 57 | |
| 58 | /* TODO: convert to link<> */ |
| 59 | cpu->apic_state->cpu = cpu; |
| 60 | cpu->apic_state->apicbase = APIC_DEFAULT_ADDRESS | MSR_IA32_APICBASE_ENABLE; |
| 61 | |
| 62 | /* cpu must be set before realize, which validates the APIC ID */ |
| 63 | qdev_prop_set_uint32(DEVICE(cpu->apic_state), "id", cpu->apic_id); |
| 64 | } |
| 65 | |
| 66 | void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) |
| 67 | { |
| 68 | static bool apic_mmio_map_once; |
| 69 | |
| 70 | if (cpu->apic_state == NULL) { |
| 71 | return; |
| 72 | } |
| 73 | qdev_realize(DEVICE(cpu->apic_state), NULL, errp); |
| 74 | |
| 75 | /* Map APIC MMIO area */ |
| 76 | if (!apic_mmio_map_once) { |
| 77 | memory_region_add_subregion_overlap(get_system_memory(), |
| 78 | cpu->apic_state->apicbase & |
| 79 | MSR_IA32_APICBASE_BASE, |
| 80 | &cpu->apic_state->io_memory, |
| 81 | 0x1000); |
| 82 | apic_mmio_map_once = true; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | #ifdef CONFIG_HMP |
| 87 | void hmp_info_local_apic(MonitorHMP *hmp, const QDict *qdict) |
| 88 | { |
| 89 | CPUState *cs; |
| 90 | |
| 91 | if (qdict_haskey(qdict, "apic-id")) { |
| 92 | int id = qdict_get_try_int(qdict, "apic-id", 0); |
| 93 | |
| 94 | cs = cpu_by_arch_id(id); |
| 95 | if (cs) { |
| 96 | cpu_synchronize_state(cs); |
| 97 | } |
| 98 | } else { |
| 99 | cs = monitor_hmp_get_cpu(hmp); |
| 100 | } |
| 101 | |
| 102 | |
| 103 | if (!cs) { |
| 104 | monitor_hmp_printf(hmp, "No CPU available\n"); |
| 105 | return; |
| 106 | } |
| 107 | x86_cpu_dump_local_apic_state(cs, CPU_DUMP_FPU); |
| 108 | } |
| 109 | #endif |