xref: /openbmc/qemu/target/i386/whpx/whpx-apic.c (revision 4c4465ff)
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-common.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 "sysemu/hw_accel.h"
20 #include "sysemu/whpx.h"
21 #include "whp-dispatch.h"
22 
23 static void whpx_put_apic_state(APICCommonState *s,
24                                 struct whpx_lapic_state *kapic)
25 {
26     int i;
27 
28     memset(kapic, 0, sizeof(*kapic));
29     kapic->fields[0x2].data = s->id << 24;
30     kapic->fields[0x3].data = s->version | ((APIC_LVT_NB - 1) << 16);
31     kapic->fields[0x8].data = s->tpr;
32     kapic->fields[0xd].data = s->log_dest << 24;
33     kapic->fields[0xe].data = s->dest_mode << 28 | 0x0fffffff;
34     kapic->fields[0xf].data = s->spurious_vec;
35     for (i = 0; i < 8; i++) {
36         kapic->fields[0x10 + i].data = s->isr[i];
37         kapic->fields[0x18 + i].data = s->tmr[i];
38         kapic->fields[0x20 + i].data = s->irr[i];
39     }
40 
41     kapic->fields[0x28].data = s->esr;
42     kapic->fields[0x30].data = s->icr[0];
43     kapic->fields[0x31].data = s->icr[1];
44     for (i = 0; i < APIC_LVT_NB; i++) {
45         kapic->fields[0x32 + i].data = s->lvt[i];
46     }
47 
48     kapic->fields[0x38].data = s->initial_count;
49     kapic->fields[0x3e].data = s->divide_conf;
50 }
51 
52 static void whpx_get_apic_state(APICCommonState *s,
53                                 struct whpx_lapic_state *kapic)
54 {
55     int i, v;
56 
57     s->id = kapic->fields[0x2].data >> 24;
58     s->tpr = kapic->fields[0x8].data;
59     s->arb_id = kapic->fields[0x9].data;
60     s->log_dest = kapic->fields[0xd].data >> 24;
61     s->dest_mode = kapic->fields[0xe].data >> 28;
62     s->spurious_vec = kapic->fields[0xf].data;
63     for (i = 0; i < 8; i++) {
64         s->isr[i] = kapic->fields[0x10 + i].data;
65         s->tmr[i] = kapic->fields[0x18 + i].data;
66         s->irr[i] = kapic->fields[0x20 + i].data;
67     }
68 
69     s->esr = kapic->fields[0x28].data;
70     s->icr[0] = kapic->fields[0x30].data;
71     s->icr[1] = kapic->fields[0x31].data;
72     for (i = 0; i < APIC_LVT_NB; i++) {
73         s->lvt[i] = kapic->fields[0x32 + i].data;
74     }
75 
76     s->initial_count = kapic->fields[0x38].data;
77     s->divide_conf = kapic->fields[0x3e].data;
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 whpx_apic_set_base(APICCommonState *s, uint64_t val)
87 {
88     s->apicbase = val;
89 }
90 
91 static void whpx_put_apic_base(CPUState *cpu, uint64_t val)
92 {
93     HRESULT hr;
94     WHV_REGISTER_VALUE reg_value = {.Reg64 = val};
95     WHV_REGISTER_NAME reg_name = WHvX64RegisterApicBase;
96 
97     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
98              whpx_global.partition,
99              cpu->cpu_index,
100              &reg_name, 1,
101              &reg_value);
102 
103     if (FAILED(hr)) {
104         error_report("WHPX: Failed to set MSR APIC base, hr=%08lx", hr);
105     }
106 }
107 
108 static void whpx_apic_set_tpr(APICCommonState *s, uint8_t val)
109 {
110     s->tpr = val;
111 }
112 
113 static uint8_t whpx_apic_get_tpr(APICCommonState *s)
114 {
115     return s->tpr;
116 }
117 
118 static void whpx_apic_vapic_base_update(APICCommonState *s)
119 {
120     /* not implemented yet */
121 }
122 
123 static void whpx_apic_put(CPUState *cs, run_on_cpu_data data)
124 {
125     APICCommonState *s = data.host_ptr;
126     struct whpx_lapic_state kapic;
127     HRESULT hr;
128 
129     whpx_put_apic_base(CPU(s->cpu), s->apicbase);
130     whpx_put_apic_state(s, &kapic);
131 
132     hr = whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2(
133         whpx_global.partition,
134         cs->cpu_index,
135         &kapic,
136         sizeof(kapic));
137     if (FAILED(hr)) {
138         fprintf(stderr,
139             "WHvSetVirtualProcessorInterruptControllerState failed: %08lx\n",
140              hr);
141 
142         abort();
143     }
144 }
145 
146 void whpx_apic_get(DeviceState *dev)
147 {
148     APICCommonState *s = APIC_COMMON(dev);
149     CPUState *cpu = CPU(s->cpu);
150     struct whpx_lapic_state kapic;
151 
152     HRESULT hr = whp_dispatch.WHvGetVirtualProcessorInterruptControllerState2(
153         whpx_global.partition,
154         cpu->cpu_index,
155         &kapic,
156         sizeof(kapic),
157         NULL);
158     if (FAILED(hr)) {
159         fprintf(stderr,
160             "WHvSetVirtualProcessorInterruptControllerState failed: %08lx\n",
161             hr);
162 
163         abort();
164     }
165 
166     whpx_get_apic_state(s, &kapic);
167 }
168 
169 static void whpx_apic_post_load(APICCommonState *s)
170 {
171     run_on_cpu(CPU(s->cpu), whpx_apic_put, RUN_ON_CPU_HOST_PTR(s));
172 }
173 
174 static void whpx_apic_external_nmi(APICCommonState *s)
175 {
176 }
177 
178 static void whpx_send_msi(MSIMessage *msg)
179 {
180     uint64_t addr = msg->address;
181     uint32_t data = msg->data;
182     uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
183     uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
184     uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
185     uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
186     uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
187 
188     WHV_INTERRUPT_CONTROL interrupt = {
189         /* Values correspond to delivery modes */
190         .Type = delivery,
191         .DestinationMode = dest_mode ?
192             WHvX64InterruptDestinationModeLogical :
193             WHvX64InterruptDestinationModePhysical,
194 
195         .TriggerMode = trigger_mode ?
196             WHvX64InterruptTriggerModeLevel : WHvX64InterruptTriggerModeEdge,
197         .Reserved = 0,
198         .Vector = vector,
199         .Destination = dest,
200     };
201     HRESULT hr = whp_dispatch.WHvRequestInterrupt(whpx_global.partition,
202                      &interrupt, sizeof(interrupt));
203     if (FAILED(hr)) {
204         fprintf(stderr, "whpx: injection failed, MSI (%llx, %x) delivery: %d, "
205                 "dest_mode: %d, trigger mode: %d, vector: %d, lost (%08lx)\n",
206                 addr, data, delivery, dest_mode, trigger_mode, vector, hr);
207     }
208 }
209 
210 static uint64_t whpx_apic_mem_read(void *opaque, hwaddr addr,
211                                    unsigned size)
212 {
213     return ~(uint64_t)0;
214 }
215 
216 static void whpx_apic_mem_write(void *opaque, hwaddr addr,
217                                 uint64_t data, unsigned size)
218 {
219     MSIMessage msg = { .address = addr, .data = data };
220     whpx_send_msi(&msg);
221 }
222 
223 static const MemoryRegionOps whpx_apic_io_ops = {
224     .read = whpx_apic_mem_read,
225     .write = whpx_apic_mem_write,
226     .endianness = DEVICE_NATIVE_ENDIAN,
227 };
228 
229 static void whpx_apic_reset(APICCommonState *s)
230 {
231     /* Not used by WHPX. */
232     s->wait_for_sipi = 0;
233 
234     run_on_cpu(CPU(s->cpu), whpx_apic_put, RUN_ON_CPU_HOST_PTR(s));
235 }
236 
237 static void whpx_apic_realize(DeviceState *dev, Error **errp)
238 {
239     APICCommonState *s = APIC_COMMON(dev);
240 
241     memory_region_init_io(&s->io_memory, OBJECT(s), &whpx_apic_io_ops, s,
242                           "whpx-apic-msi", APIC_SPACE_SIZE);
243 
244     msi_nonbroken = true;
245 }
246 
247 static void whpx_apic_class_init(ObjectClass *klass, void *data)
248 {
249     APICCommonClass *k = APIC_COMMON_CLASS(klass);
250 
251     k->realize = whpx_apic_realize;
252     k->reset = whpx_apic_reset;
253     k->set_base = whpx_apic_set_base;
254     k->set_tpr = whpx_apic_set_tpr;
255     k->get_tpr = whpx_apic_get_tpr;
256     k->post_load = whpx_apic_post_load;
257     k->vapic_base_update = whpx_apic_vapic_base_update;
258     k->external_nmi = whpx_apic_external_nmi;
259     k->send_msi = whpx_send_msi;
260 }
261 
262 static const TypeInfo whpx_apic_info = {
263     .name = "whpx-apic",
264     .parent = TYPE_APIC_COMMON,
265     .instance_size = sizeof(APICCommonState),
266     .class_init = whpx_apic_class_init,
267 };
268 
269 static void whpx_apic_register_types(void)
270 {
271     type_register_static(&whpx_apic_info);
272 }
273 
274 type_init(whpx_apic_register_types)
275