| 1 | /* |
| 2 | * WHPX platform APIC support |
| 3 | * |
| 4 | * Copyright (c) 2011 Siemens AG |
| 5 | * |
| 6 | * Authors: |
| 7 | * Jan Kiszka <jan.kiszka@siemens.com> |
| 8 | * John Starks <jostarks@microsoft.com> |
| 9 | * |
| 10 | * This work is licensed under the terms of the GNU GPL version 2. |
| 11 | * See the COPYING file in the top-level directory. |
| 12 | */ |
| 13 | #include "qemu/osdep.h" |
| 14 | #include "qemu/error-report.h" |
| 15 | #include "cpu.h" |
| 16 | #include "hw/i386/apic_internal.h" |
| 17 | #include "hw/i386/apic-msidef.h" |
| 18 | #include "hw/pci/msi.h" |
| 19 | #include "system/hw_accel.h" |
| 20 | #include "system/whpx.h" |
| 21 | #include "system/whpx-internal.h" |
| 22 | |
| 23 | struct whpx_lapic_state { |
| 24 | struct { |
| 25 | uint32_t data; |
| 26 | uint32_t padding[3]; |
| 27 | } fields[256]; |
| 28 | }; |
| 29 | |
| 30 | static void whpx_put_apic_state(APICCommonState *s, |
| 31 | struct whpx_lapic_state *kapic) |
| 32 | { |
| 33 | int i; |
| 34 | |
| 35 | memset(kapic, 0, sizeof(*kapic)); |
| 36 | if (s->apicbase & MSR_IA32_APICBASE_EXTD) { |
| 37 | kapic->fields[0x2].data = s->initial_apic_id; |
| 38 | } else { |
| 39 | kapic->fields[0x2].data = s->id << 24; |
| 40 | } |
| 41 | kapic->fields[0x3].data = s->version | ((APIC_LVT_NB - 1) << 16); |
| 42 | kapic->fields[0x8].data = s->tpr; |
| 43 | kapic->fields[0xd].data = s->log_dest << 24; |
| 44 | kapic->fields[0xe].data = s->dest_mode << 28 | 0x0fffffff; |
| 45 | kapic->fields[0xf].data = s->spurious_vec; |
| 46 | for (i = 0; i < 8; i++) { |
| 47 | kapic->fields[0x10 + i].data = s->isr[i]; |
| 48 | kapic->fields[0x18 + i].data = s->tmr[i]; |
| 49 | kapic->fields[0x20 + i].data = s->irr[i]; |
| 50 | } |
| 51 | |
| 52 | kapic->fields[0x28].data = s->esr; |
| 53 | kapic->fields[0x30].data = s->icr[0]; |
| 54 | kapic->fields[0x31].data = s->icr[1]; |
| 55 | for (i = 0; i < APIC_LVT_NB; i++) { |
| 56 | kapic->fields[0x32 + i].data = s->lvt[i]; |
| 57 | } |
| 58 | |
| 59 | kapic->fields[0x38].data = s->initial_count; |
| 60 | kapic->fields[0x3e].data = s->divide_conf; |
| 61 | } |
| 62 | |
| 63 | static void whpx_get_apic_state(APICCommonState *s, |
| 64 | struct whpx_lapic_state *kapic) |
| 65 | { |
| 66 | int i, v; |
| 67 | |
| 68 | if (s->apicbase & MSR_IA32_APICBASE_EXTD) { |
| 69 | assert(kapic->fields[0x2].data == s->initial_apic_id); |
| 70 | } else { |
| 71 | s->id = kapic->fields[0x2].data >> 24; |
| 72 | } |
| 73 | s->tpr = kapic->fields[0x8].data; |
| 74 | s->arb_id = kapic->fields[0x9].data; |
| 75 | s->log_dest = kapic->fields[0xd].data >> 24; |
| 76 | s->dest_mode = kapic->fields[0xe].data >> 28; |
| 77 | s->spurious_vec = kapic->fields[0xf].data; |
| 78 | for (i = 0; i < 8; i++) { |
| 79 | s->isr[i] = kapic->fields[0x10 + i].data; |
| 80 | s->tmr[i] = kapic->fields[0x18 + i].data; |
| 81 | s->irr[i] = kapic->fields[0x20 + i].data; |
| 82 | } |
| 83 | |
| 84 | s->esr = kapic->fields[0x28].data; |
| 85 | s->icr[0] = kapic->fields[0x30].data; |
| 86 | s->icr[1] = kapic->fields[0x31].data; |
| 87 | for (i = 0; i < APIC_LVT_NB; i++) { |
| 88 | s->lvt[i] = kapic->fields[0x32 + i].data; |
| 89 | } |
| 90 | |
| 91 | s->initial_count = kapic->fields[0x38].data; |
| 92 | s->divide_conf = kapic->fields[0x3e].data; |
| 93 | |
| 94 | v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); |
| 95 | s->count_shift = (v + 1) & 7; |
| 96 | |
| 97 | s->initial_count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
| 98 | apic_next_timer(s, s->initial_count_load_time); |
| 99 | } |
| 100 | |
| 101 | static int apic_set_base_check(APICCommonState *s, uint64_t val) |
| 102 | { |
| 103 | /* Enable x2apic when x2apic is not supported by CPU */ |
| 104 | if (!cpu_has_x2apic_feature(&s->cpu->env) && |
| 105 | val & MSR_IA32_APICBASE_EXTD) { |
| 106 | return -1; |
| 107 | } |
| 108 | |
| 109 | /* |
| 110 | * Transition into invalid state |
| 111 | * (s->apicbase & MSR_IA32_APICBASE_ENABLE == 0) && |
| 112 | * (s->apicbase & MSR_IA32_APICBASE_EXTD) == 1 |
| 113 | */ |
| 114 | if (!(val & MSR_IA32_APICBASE_ENABLE) && |
| 115 | (val & MSR_IA32_APICBASE_EXTD)) { |
| 116 | return -1; |
| 117 | } |
| 118 | |
| 119 | /* Invalid transition from disabled mode to x2APIC */ |
| 120 | if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) && |
| 121 | !(s->apicbase & MSR_IA32_APICBASE_EXTD) && |
| 122 | (val & MSR_IA32_APICBASE_ENABLE) && |
| 123 | (val & MSR_IA32_APICBASE_EXTD)) { |
| 124 | return -1; |
| 125 | } |
| 126 | |
| 127 | /* Invalid transition from x2APIC to xAPIC */ |
| 128 | if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) && |
| 129 | (s->apicbase & MSR_IA32_APICBASE_EXTD) && |
| 130 | (val & MSR_IA32_APICBASE_ENABLE) && |
| 131 | !(val & MSR_IA32_APICBASE_EXTD)) { |
| 132 | return -1; |
| 133 | } |
| 134 | |
| 135 | return 0; |
| 136 | } |
| 137 | |
| 138 | static int apic_set_base(APICCommonState *s, uint64_t val) |
| 139 | { |
| 140 | if (apic_set_base_check(s, val) < 0) { |
| 141 | return -1; |
| 142 | } |
| 143 | |
| 144 | s->apicbase = (val & MSR_IA32_APICBASE_BASE) | |
| 145 | (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); |
| 146 | if (!(val & MSR_IA32_APICBASE_ENABLE)) { |
| 147 | s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; |
| 148 | cpu_clear_apic_feature(&s->cpu->env); |
| 149 | } |
| 150 | |
| 151 | /* Transition from disabled mode to xAPIC */ |
| 152 | if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) && |
| 153 | (val & MSR_IA32_APICBASE_ENABLE)) { |
| 154 | s->apicbase |= MSR_IA32_APICBASE_ENABLE; |
| 155 | cpu_set_apic_feature(&s->cpu->env); |
| 156 | } |
| 157 | |
| 158 | /* Transition from xAPIC to x2APIC */ |
| 159 | if (cpu_has_x2apic_feature(&s->cpu->env) && |
| 160 | !(s->apicbase & MSR_IA32_APICBASE_EXTD) && |
| 161 | (val & MSR_IA32_APICBASE_EXTD)) { |
| 162 | s->apicbase |= MSR_IA32_APICBASE_EXTD; |
| 163 | } |
| 164 | |
| 165 | return 0; |
| 166 | } |
| 167 | |
| 168 | static void whpx_put_apic_base(CPUState *cpu, uint64_t val) |
| 169 | { |
| 170 | HRESULT hr; |
| 171 | WHV_REGISTER_VALUE reg_value = {.Reg64 = val}; |
| 172 | WHV_REGISTER_NAME reg_name = WHvX64RegisterApicBase; |
| 173 | |
| 174 | hr = whp_dispatch.WHvSetVirtualProcessorRegisters( |
| 175 | whpx_global.partition, |
| 176 | cpu->cpu_index, |
| 177 | ®_name, 1, |
| 178 | ®_value); |
| 179 | |
| 180 | if (FAILED(hr)) { |
| 181 | error_report("WHPX: Failed to set MSR APIC base, hr=%08lx", hr); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | static void whpx_apic_set_tpr(APICCommonState *s, uint8_t val) |
| 186 | { |
| 187 | s->tpr = val; |
| 188 | } |
| 189 | |
| 190 | static uint8_t whpx_apic_get_tpr(APICCommonState *s) |
| 191 | { |
| 192 | return s->tpr; |
| 193 | } |
| 194 | |
| 195 | static void whpx_apic_vapic_base_update(APICCommonState *s) |
| 196 | { |
| 197 | /* not implemented yet */ |
| 198 | } |
| 199 | |
| 200 | static void whpx_apic_put(CPUState *cs, run_on_cpu_data data) |
| 201 | { |
| 202 | APICCommonState *s = data.host_ptr; |
| 203 | struct whpx_lapic_state kapic; |
| 204 | HRESULT hr; |
| 205 | |
| 206 | whpx_put_apic_base(CPU(s->cpu), s->apicbase); |
| 207 | whpx_put_apic_state(s, &kapic); |
| 208 | |
| 209 | hr = whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2( |
| 210 | whpx_global.partition, |
| 211 | cs->cpu_index, |
| 212 | &kapic, |
| 213 | sizeof(kapic)); |
| 214 | if (FAILED(hr)) { |
| 215 | fprintf(stderr, |
| 216 | "WHvSetVirtualProcessorInterruptControllerState failed: %08lx\n", |
| 217 | hr); |
| 218 | |
| 219 | abort(); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | void whpx_apic_get(APICCommonState *s) |
| 224 | { |
| 225 | CPUState *cpu = CPU(s->cpu); |
| 226 | struct whpx_lapic_state kapic; |
| 227 | |
| 228 | HRESULT hr = whp_dispatch.WHvGetVirtualProcessorInterruptControllerState2( |
| 229 | whpx_global.partition, |
| 230 | cpu->cpu_index, |
| 231 | &kapic, |
| 232 | sizeof(kapic), |
| 233 | NULL); |
| 234 | if (FAILED(hr)) { |
| 235 | fprintf(stderr, |
| 236 | "WHvSetVirtualProcessorInterruptControllerState failed: %08lx\n", |
| 237 | hr); |
| 238 | |
| 239 | abort(); |
| 240 | } |
| 241 | |
| 242 | whpx_get_apic_state(s, &kapic); |
| 243 | } |
| 244 | |
| 245 | static void whpx_apic_post_load(APICCommonState *s) |
| 246 | { |
| 247 | run_on_cpu(CPU(s->cpu), whpx_apic_put, RUN_ON_CPU_HOST_PTR(s)); |
| 248 | } |
| 249 | |
| 250 | static void whpx_apic_external_nmi(APICCommonState *s) |
| 251 | { |
| 252 | } |
| 253 | |
| 254 | static void whpx_send_msi(MSIMessage *msg) |
| 255 | { |
| 256 | uint64_t addr = msg->address; |
| 257 | uint32_t data = msg->data; |
| 258 | uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; |
| 259 | uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; |
| 260 | uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; |
| 261 | uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; |
| 262 | uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; |
| 263 | |
| 264 | if (vector == 0) { |
| 265 | warn_report("Ignoring request for interrupt vector 0"); |
| 266 | return; |
| 267 | } |
| 268 | |
| 269 | WHV_INTERRUPT_CONTROL interrupt = { |
| 270 | /* Values correspond to delivery modes */ |
| 271 | .Type = delivery, |
| 272 | .DestinationMode = dest_mode ? |
| 273 | WHvX64InterruptDestinationModeLogical : |
| 274 | WHvX64InterruptDestinationModePhysical, |
| 275 | |
| 276 | .TriggerMode = trigger_mode ? |
| 277 | WHvX64InterruptTriggerModeLevel : WHvX64InterruptTriggerModeEdge, |
| 278 | .Reserved = 0, |
| 279 | .Vector = vector, |
| 280 | .Destination = dest, |
| 281 | }; |
| 282 | HRESULT hr = whp_dispatch.WHvRequestInterrupt(whpx_global.partition, |
| 283 | &interrupt, sizeof(interrupt)); |
| 284 | if (FAILED(hr)) { |
| 285 | fprintf(stderr, "whpx: injection failed, MSI (%llx, %x) delivery: %d, " |
| 286 | "dest_mode: %d, trigger mode: %d, vector: %d, lost (%08lx)\n", |
| 287 | addr, data, delivery, dest_mode, trigger_mode, vector, hr); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | static uint64_t whpx_apic_mem_read(void *opaque, hwaddr addr, |
| 292 | unsigned size) |
| 293 | { |
| 294 | return ~(uint64_t)0; |
| 295 | } |
| 296 | |
| 297 | static void whpx_apic_mem_write(void *opaque, hwaddr addr, |
| 298 | uint64_t data, unsigned size) |
| 299 | { |
| 300 | MSIMessage msg = { .address = addr, .data = data }; |
| 301 | whpx_send_msi(&msg); |
| 302 | } |
| 303 | |
| 304 | static const MemoryRegionOps whpx_apic_io_ops = { |
| 305 | .read = whpx_apic_mem_read, |
| 306 | .write = whpx_apic_mem_write, |
| 307 | .impl.min_access_size = 1, |
| 308 | .impl.max_access_size = 4, |
| 309 | .valid.min_access_size = 1, |
| 310 | .valid.max_access_size = 4, |
| 311 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 312 | }; |
| 313 | |
| 314 | static void whpx_apic_reset(APICCommonState *s) |
| 315 | { |
| 316 | /* Not used by WHPX. */ |
| 317 | s->wait_for_sipi = 0; |
| 318 | |
| 319 | run_on_cpu(CPU(s->cpu), whpx_apic_put, RUN_ON_CPU_HOST_PTR(s)); |
| 320 | } |
| 321 | |
| 322 | static void whpx_apic_realize(DeviceState *dev, Error **errp) |
| 323 | { |
| 324 | APICCommonState *s = APIC_COMMON(dev); |
| 325 | |
| 326 | memory_region_init_io(&s->io_memory, OBJECT(s), &whpx_apic_io_ops, s, |
| 327 | "whpx-apic-msi", APIC_SPACE_SIZE); |
| 328 | |
| 329 | msi_nonbroken = true; |
| 330 | } |
| 331 | |
| 332 | static void whpx_apic_class_init(ObjectClass *klass, const void *data) |
| 333 | { |
| 334 | APICCommonClass *k = APIC_COMMON_CLASS(klass); |
| 335 | |
| 336 | k->realize = whpx_apic_realize; |
| 337 | k->reset = whpx_apic_reset; |
| 338 | k->set_base = apic_set_base; |
| 339 | k->set_tpr = whpx_apic_set_tpr; |
| 340 | k->get_tpr = whpx_apic_get_tpr; |
| 341 | k->post_load = whpx_apic_post_load; |
| 342 | k->vapic_base_update = whpx_apic_vapic_base_update; |
| 343 | k->external_nmi = whpx_apic_external_nmi; |
| 344 | k->send_msi = whpx_send_msi; |
| 345 | } |
| 346 | |
| 347 | static const TypeInfo whpx_apic_info = { |
| 348 | .name = "whpx-apic", |
| 349 | .parent = TYPE_APIC_COMMON, |
| 350 | .instance_size = sizeof(APICCommonState), |
| 351 | .class_init = whpx_apic_class_init, |
| 352 | }; |
| 353 | |
| 354 | static void whpx_apic_register_types(void) |
| 355 | { |
| 356 | type_register_static(&whpx_apic_info); |
| 357 | } |
| 358 | |
| 359 | type_init(whpx_apic_register_types) |