1 /* 2 * KVM in-kernel APIC support 3 * 4 * Copyright (c) 2011 Siemens AG 5 * 6 * Authors: 7 * Jan Kiszka <jan.kiszka@siemens.com> 8 * 9 * This work is licensed under the terms of the GNU GPL version 2. 10 * See the COPYING file in the top-level directory. 11 */ 12 #include "hw/i386/apic_internal.h" 13 #include "hw/pci/msi.h" 14 #include "sysemu/kvm.h" 15 16 static inline void kvm_apic_set_reg(struct kvm_lapic_state *kapic, 17 int reg_id, uint32_t val) 18 { 19 *((uint32_t *)(kapic->regs + (reg_id << 4))) = val; 20 } 21 22 static inline uint32_t kvm_apic_get_reg(struct kvm_lapic_state *kapic, 23 int reg_id) 24 { 25 return *((uint32_t *)(kapic->regs + (reg_id << 4))); 26 } 27 28 void kvm_put_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic) 29 { 30 APICCommonState *s = APIC_COMMON(dev); 31 int i; 32 33 memset(kapic, 0, sizeof(*kapic)); 34 kvm_apic_set_reg(kapic, 0x2, s->id << 24); 35 kvm_apic_set_reg(kapic, 0x8, s->tpr); 36 kvm_apic_set_reg(kapic, 0xd, s->log_dest << 24); 37 kvm_apic_set_reg(kapic, 0xe, s->dest_mode << 28 | 0x0fffffff); 38 kvm_apic_set_reg(kapic, 0xf, s->spurious_vec); 39 for (i = 0; i < 8; i++) { 40 kvm_apic_set_reg(kapic, 0x10 + i, s->isr[i]); 41 kvm_apic_set_reg(kapic, 0x18 + i, s->tmr[i]); 42 kvm_apic_set_reg(kapic, 0x20 + i, s->irr[i]); 43 } 44 kvm_apic_set_reg(kapic, 0x28, s->esr); 45 kvm_apic_set_reg(kapic, 0x30, s->icr[0]); 46 kvm_apic_set_reg(kapic, 0x31, s->icr[1]); 47 for (i = 0; i < APIC_LVT_NB; i++) { 48 kvm_apic_set_reg(kapic, 0x32 + i, s->lvt[i]); 49 } 50 kvm_apic_set_reg(kapic, 0x38, s->initial_count); 51 kvm_apic_set_reg(kapic, 0x3e, s->divide_conf); 52 } 53 54 void kvm_get_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic) 55 { 56 APICCommonState *s = APIC_COMMON(dev); 57 int i, v; 58 59 s->id = kvm_apic_get_reg(kapic, 0x2) >> 24; 60 s->tpr = kvm_apic_get_reg(kapic, 0x8); 61 s->arb_id = kvm_apic_get_reg(kapic, 0x9); 62 s->log_dest = kvm_apic_get_reg(kapic, 0xd) >> 24; 63 s->dest_mode = kvm_apic_get_reg(kapic, 0xe) >> 28; 64 s->spurious_vec = kvm_apic_get_reg(kapic, 0xf); 65 for (i = 0; i < 8; i++) { 66 s->isr[i] = kvm_apic_get_reg(kapic, 0x10 + i); 67 s->tmr[i] = kvm_apic_get_reg(kapic, 0x18 + i); 68 s->irr[i] = kvm_apic_get_reg(kapic, 0x20 + i); 69 } 70 s->esr = kvm_apic_get_reg(kapic, 0x28); 71 s->icr[0] = kvm_apic_get_reg(kapic, 0x30); 72 s->icr[1] = kvm_apic_get_reg(kapic, 0x31); 73 for (i = 0; i < APIC_LVT_NB; i++) { 74 s->lvt[i] = kvm_apic_get_reg(kapic, 0x32 + i); 75 } 76 s->initial_count = kvm_apic_get_reg(kapic, 0x38); 77 s->divide_conf = kvm_apic_get_reg(kapic, 0x3e); 78 79 v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); 80 s->count_shift = (v + 1) & 7; 81 82 s->initial_count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 83 apic_next_timer(s, s->initial_count_load_time); 84 } 85 86 static void kvm_apic_set_base(APICCommonState *s, uint64_t val) 87 { 88 s->apicbase = val; 89 } 90 91 static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val) 92 { 93 s->tpr = (val & 0x0f) << 4; 94 } 95 96 static uint8_t kvm_apic_get_tpr(APICCommonState *s) 97 { 98 return s->tpr >> 4; 99 } 100 101 static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable) 102 { 103 struct kvm_tpr_access_ctl ctl = { 104 .enabled = enable 105 }; 106 107 kvm_vcpu_ioctl(CPU(s->cpu), KVM_TPR_ACCESS_REPORTING, &ctl); 108 } 109 110 static void kvm_apic_vapic_base_update(APICCommonState *s) 111 { 112 struct kvm_vapic_addr vapid_addr = { 113 .vapic_addr = s->vapic_paddr, 114 }; 115 int ret; 116 117 ret = kvm_vcpu_ioctl(CPU(s->cpu), KVM_SET_VAPIC_ADDR, &vapid_addr); 118 if (ret < 0) { 119 fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n", 120 strerror(-ret)); 121 abort(); 122 } 123 } 124 125 static void do_inject_external_nmi(void *data) 126 { 127 APICCommonState *s = data; 128 CPUState *cpu = CPU(s->cpu); 129 uint32_t lvt; 130 int ret; 131 132 cpu_synchronize_state(cpu); 133 134 lvt = s->lvt[APIC_LVT_LINT1]; 135 if (!(lvt & APIC_LVT_MASKED) && ((lvt >> 8) & 7) == APIC_DM_NMI) { 136 ret = kvm_vcpu_ioctl(cpu, KVM_NMI); 137 if (ret < 0) { 138 fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n", 139 strerror(-ret)); 140 } 141 } 142 } 143 144 static void kvm_apic_external_nmi(APICCommonState *s) 145 { 146 run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s); 147 } 148 149 static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr, 150 unsigned size) 151 { 152 return ~(uint64_t)0; 153 } 154 155 static void kvm_apic_mem_write(void *opaque, hwaddr addr, 156 uint64_t data, unsigned size) 157 { 158 MSIMessage msg = { .address = addr, .data = data }; 159 int ret; 160 161 ret = kvm_irqchip_send_msi(kvm_state, msg); 162 if (ret < 0) { 163 fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n", 164 strerror(-ret)); 165 } 166 } 167 168 static const MemoryRegionOps kvm_apic_io_ops = { 169 .read = kvm_apic_mem_read, 170 .write = kvm_apic_mem_write, 171 .endianness = DEVICE_NATIVE_ENDIAN, 172 }; 173 174 static void kvm_apic_reset(APICCommonState *s) 175 { 176 /* Not used by KVM, which uses the CPU mp_state instead. */ 177 s->wait_for_sipi = 0; 178 } 179 180 static void kvm_apic_realize(DeviceState *dev, Error **errp) 181 { 182 APICCommonState *s = APIC_COMMON(dev); 183 184 memory_region_init_io(&s->io_memory, NULL, &kvm_apic_io_ops, s, "kvm-apic-msi", 185 APIC_SPACE_SIZE); 186 187 if (kvm_has_gsi_routing()) { 188 msi_supported = true; 189 } 190 } 191 192 static void kvm_apic_class_init(ObjectClass *klass, void *data) 193 { 194 APICCommonClass *k = APIC_COMMON_CLASS(klass); 195 196 k->realize = kvm_apic_realize; 197 k->reset = kvm_apic_reset; 198 k->set_base = kvm_apic_set_base; 199 k->set_tpr = kvm_apic_set_tpr; 200 k->get_tpr = kvm_apic_get_tpr; 201 k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting; 202 k->vapic_base_update = kvm_apic_vapic_base_update; 203 k->external_nmi = kvm_apic_external_nmi; 204 } 205 206 static const TypeInfo kvm_apic_info = { 207 .name = "kvm-apic", 208 .parent = TYPE_APIC_COMMON, 209 .instance_size = sizeof(APICCommonState), 210 .class_init = kvm_apic_class_init, 211 }; 212 213 static void kvm_apic_register_types(void) 214 { 215 type_register_static(&kvm_apic_info); 216 } 217 218 type_init(kvm_apic_register_types) 219