]>
Commit | Line | Data |
---|---|---|
dae01685 JK |
1 | /* |
2 | * APIC support - common bits of emulated and KVM kernel model | |
3 | * | |
4 | * Copyright (c) 2004-2005 Fabrice Bellard | |
5 | * Copyright (c) 2011 Jan Kiszka, Siemens AG | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/> | |
19 | */ | |
20 | #include "apic.h" | |
21 | #include "apic_internal.h" | |
22 | #include "trace.h" | |
23 | ||
24 | static int apic_irq_delivered; | |
25 | ||
26 | void cpu_set_apic_base(DeviceState *d, uint64_t val) | |
27 | { | |
28 | APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); | |
29 | APICCommonInfo *info; | |
30 | ||
31 | trace_cpu_set_apic_base(val); | |
32 | ||
33 | if (s) { | |
34 | info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); | |
35 | info->set_base(s, val); | |
36 | } | |
37 | } | |
38 | ||
39 | uint64_t cpu_get_apic_base(DeviceState *d) | |
40 | { | |
41 | APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); | |
42 | ||
43 | trace_cpu_get_apic_base(s ? (uint64_t)s->apicbase : 0); | |
44 | ||
45 | return s ? s->apicbase : 0; | |
46 | } | |
47 | ||
48 | void cpu_set_apic_tpr(DeviceState *d, uint8_t val) | |
49 | { | |
50 | APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); | |
51 | APICCommonInfo *info; | |
52 | ||
53 | if (s) { | |
54 | info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); | |
55 | info->set_tpr(s, val); | |
56 | } | |
57 | } | |
58 | ||
59 | uint8_t cpu_get_apic_tpr(DeviceState *d) | |
60 | { | |
61 | APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); | |
62 | ||
63 | return s ? s->tpr >> 4 : 0; | |
64 | } | |
65 | ||
66 | void apic_report_irq_delivered(int delivered) | |
67 | { | |
68 | apic_irq_delivered += delivered; | |
69 | ||
70 | trace_apic_report_irq_delivered(apic_irq_delivered); | |
71 | } | |
72 | ||
73 | void apic_reset_irq_delivered(void) | |
74 | { | |
75 | trace_apic_reset_irq_delivered(apic_irq_delivered); | |
76 | ||
77 | apic_irq_delivered = 0; | |
78 | } | |
79 | ||
80 | int apic_get_irq_delivered(void) | |
81 | { | |
82 | trace_apic_get_irq_delivered(apic_irq_delivered); | |
83 | ||
84 | return apic_irq_delivered; | |
85 | } | |
86 | ||
87 | void apic_deliver_nmi(DeviceState *d) | |
88 | { | |
89 | APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); | |
90 | APICCommonInfo *info; | |
91 | ||
92 | info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); | |
93 | info->external_nmi(s); | |
94 | } | |
95 | ||
96 | void apic_init_reset(DeviceState *d) | |
97 | { | |
98 | APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); | |
99 | int i; | |
100 | ||
101 | if (!s) { | |
102 | return; | |
103 | } | |
104 | s->tpr = 0; | |
105 | s->spurious_vec = 0xff; | |
106 | s->log_dest = 0; | |
107 | s->dest_mode = 0xf; | |
108 | memset(s->isr, 0, sizeof(s->isr)); | |
109 | memset(s->tmr, 0, sizeof(s->tmr)); | |
110 | memset(s->irr, 0, sizeof(s->irr)); | |
111 | for (i = 0; i < APIC_LVT_NB; i++) { | |
112 | s->lvt[i] = APIC_LVT_MASKED; | |
113 | } | |
114 | s->esr = 0; | |
115 | memset(s->icr, 0, sizeof(s->icr)); | |
116 | s->divide_conf = 0; | |
117 | s->count_shift = 0; | |
118 | s->initial_count = 0; | |
119 | s->initial_count_load_time = 0; | |
120 | s->next_time = 0; | |
121 | s->wait_for_sipi = 1; | |
122 | ||
123 | qemu_del_timer(s->timer); | |
124 | } | |
125 | ||
126 | static void apic_reset_common(DeviceState *d) | |
127 | { | |
128 | APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); | |
129 | bool bsp; | |
130 | ||
131 | bsp = cpu_is_bsp(s->cpu_env); | |
132 | s->apicbase = 0xfee00000 | | |
133 | (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE; | |
134 | ||
135 | apic_init_reset(d); | |
136 | ||
137 | if (bsp) { | |
138 | /* | |
139 | * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization | |
140 | * time typically by BIOS, so PIC interrupt can be delivered to the | |
141 | * processor when local APIC is enabled. | |
142 | */ | |
143 | s->lvt[APIC_LVT_LINT0] = 0x700; | |
144 | } | |
145 | } | |
146 | ||
147 | /* This function is only used for old state version 1 and 2 */ | |
148 | static int apic_load_old(QEMUFile *f, void *opaque, int version_id) | |
149 | { | |
150 | APICCommonState *s = opaque; | |
151 | int i; | |
152 | ||
153 | if (version_id > 2) { | |
154 | return -EINVAL; | |
155 | } | |
156 | ||
157 | /* XXX: what if the base changes? (registered memory regions) */ | |
158 | qemu_get_be32s(f, &s->apicbase); | |
159 | qemu_get_8s(f, &s->id); | |
160 | qemu_get_8s(f, &s->arb_id); | |
161 | qemu_get_8s(f, &s->tpr); | |
162 | qemu_get_be32s(f, &s->spurious_vec); | |
163 | qemu_get_8s(f, &s->log_dest); | |
164 | qemu_get_8s(f, &s->dest_mode); | |
165 | for (i = 0; i < 8; i++) { | |
166 | qemu_get_be32s(f, &s->isr[i]); | |
167 | qemu_get_be32s(f, &s->tmr[i]); | |
168 | qemu_get_be32s(f, &s->irr[i]); | |
169 | } | |
170 | for (i = 0; i < APIC_LVT_NB; i++) { | |
171 | qemu_get_be32s(f, &s->lvt[i]); | |
172 | } | |
173 | qemu_get_be32s(f, &s->esr); | |
174 | qemu_get_be32s(f, &s->icr[0]); | |
175 | qemu_get_be32s(f, &s->icr[1]); | |
176 | qemu_get_be32s(f, &s->divide_conf); | |
177 | s->count_shift = qemu_get_be32(f); | |
178 | qemu_get_be32s(f, &s->initial_count); | |
179 | s->initial_count_load_time = qemu_get_be64(f); | |
180 | s->next_time = qemu_get_be64(f); | |
181 | ||
182 | if (version_id >= 2) { | |
183 | qemu_get_timer(f, s->timer); | |
184 | } | |
185 | return 0; | |
186 | } | |
187 | ||
188 | static int apic_init_common(SysBusDevice *dev) | |
189 | { | |
190 | APICCommonState *s = FROM_SYSBUS(APICCommonState, dev); | |
191 | APICCommonInfo *info; | |
192 | static int apic_no; | |
193 | ||
194 | if (apic_no >= MAX_APICS) { | |
195 | return -1; | |
196 | } | |
197 | s->idx = apic_no++; | |
198 | ||
199 | info = DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info); | |
200 | info->init(s); | |
201 | ||
202 | sysbus_init_mmio(&s->busdev, &s->io_memory); | |
203 | return 0; | |
204 | } | |
205 | ||
206 | static const VMStateDescription vmstate_apic_common = { | |
207 | .name = "apic", | |
208 | .version_id = 3, | |
209 | .minimum_version_id = 3, | |
210 | .minimum_version_id_old = 1, | |
211 | .load_state_old = apic_load_old, | |
212 | .fields = (VMStateField[]) { | |
213 | VMSTATE_UINT32(apicbase, APICCommonState), | |
214 | VMSTATE_UINT8(id, APICCommonState), | |
215 | VMSTATE_UINT8(arb_id, APICCommonState), | |
216 | VMSTATE_UINT8(tpr, APICCommonState), | |
217 | VMSTATE_UINT32(spurious_vec, APICCommonState), | |
218 | VMSTATE_UINT8(log_dest, APICCommonState), | |
219 | VMSTATE_UINT8(dest_mode, APICCommonState), | |
220 | VMSTATE_UINT32_ARRAY(isr, APICCommonState, 8), | |
221 | VMSTATE_UINT32_ARRAY(tmr, APICCommonState, 8), | |
222 | VMSTATE_UINT32_ARRAY(irr, APICCommonState, 8), | |
223 | VMSTATE_UINT32_ARRAY(lvt, APICCommonState, APIC_LVT_NB), | |
224 | VMSTATE_UINT32(esr, APICCommonState), | |
225 | VMSTATE_UINT32_ARRAY(icr, APICCommonState, 2), | |
226 | VMSTATE_UINT32(divide_conf, APICCommonState), | |
227 | VMSTATE_INT32(count_shift, APICCommonState), | |
228 | VMSTATE_UINT32(initial_count, APICCommonState), | |
229 | VMSTATE_INT64(initial_count_load_time, APICCommonState), | |
230 | VMSTATE_INT64(next_time, APICCommonState), | |
231 | VMSTATE_TIMER(timer, APICCommonState), | |
232 | VMSTATE_END_OF_LIST() | |
233 | } | |
234 | }; | |
235 | ||
236 | static Property apic_properties_common[] = { | |
237 | DEFINE_PROP_UINT8("id", APICCommonState, id, -1), | |
238 | DEFINE_PROP_PTR("cpu_env", APICCommonState, cpu_env), | |
239 | DEFINE_PROP_END_OF_LIST(), | |
240 | }; | |
241 | ||
242 | ||
243 | void apic_qdev_register(APICCommonInfo *info) | |
244 | { | |
245 | info->busdev.init = apic_init_common; | |
246 | info->busdev.qdev.size = sizeof(APICCommonState), | |
247 | info->busdev.qdev.vmsd = &vmstate_apic_common; | |
248 | info->busdev.qdev.reset = apic_reset_common; | |
249 | info->busdev.qdev.no_user = 1; | |
250 | info->busdev.qdev.props = apic_properties_common; | |
251 | sysbus_register_withprop(&info->busdev); | |
252 | } |