debuggers.hg

view xen/arch/x86/hvm/vioapic.c @ 19826:2f9e1348aa98

x86_64: allow more vCPU-s per guest

Since the shared info layout is fixed, guests are required to use
VCPUOP_register_vcpu_info prior to booting any vCPU beyond the
traditional limit of 32.

MAX_VIRT_CPUS, being an implemetation detail of the hypervisor, is no
longer being exposed in the public headers.

The tools changes are clearly incomplete (and done only so things
would
build again), and the current state of the tools (using scalar
variables all over the place to represent vCPU bitmaps) very likely
doesn't permit booting DomU-s with more than the traditional number of
vCPU-s. Testing of the extended functionality was done with Dom0 (96
vCPU-s, as well as 128 vCPU-s out of which the kernel elected - by way
of a simple kernel side patch - to use only some, resulting in a
sparse
bitmap).

ia64 changes only to make things build, and build-tested only (and the
tools part only as far as the build would go without encountering
unrelated problems in the blktap code).

Signed-off-by: Jan Beulich <jbeulich@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jun 18 10:14:16 2009 +0100 (2009-06-18)
parents ae891977a4d3
children 80839a223746
line source
1 /*
2 * Copyright (C) 2001 MandrakeSoft S.A.
3 *
4 * MandrakeSoft S.A.
5 * 43, rue d'Aboukir
6 * 75002 Paris - France
7 * http://www.linux-mandrake.com/
8 * http://www.mandrakesoft.com/
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 * Yunhong Jiang <yunhong.jiang@intel.com>
25 * Ported to xen by using virtual IRQ line.
26 */
28 #include <xen/config.h>
29 #include <xen/types.h>
30 #include <xen/mm.h>
31 #include <xen/xmalloc.h>
32 #include <xen/lib.h>
33 #include <xen/errno.h>
34 #include <xen/sched.h>
35 #include <public/hvm/ioreq.h>
36 #include <asm/hvm/io.h>
37 #include <asm/hvm/vpic.h>
38 #include <asm/hvm/vlapic.h>
39 #include <asm/hvm/support.h>
40 #include <asm/current.h>
41 #include <asm/event.h>
43 /* HACK: Route IRQ0 only to VCPU0 to prevent time jumps. */
44 #define IRQ0_SPECIAL_ROUTING 1
46 static void vioapic_deliver(struct hvm_hw_vioapic *vioapic, int irq);
48 static unsigned long vioapic_read_indirect(struct hvm_hw_vioapic *vioapic,
49 unsigned long addr,
50 unsigned long length)
51 {
52 unsigned long result = 0;
54 switch ( vioapic->ioregsel )
55 {
56 case VIOAPIC_REG_VERSION:
57 result = ((((VIOAPIC_NUM_PINS-1) & 0xff) << 16)
58 | (VIOAPIC_VERSION_ID & 0xff));
59 break;
61 #if !VIOAPIC_IS_IOSAPIC
62 case VIOAPIC_REG_APIC_ID:
63 case VIOAPIC_REG_ARB_ID:
64 result = ((vioapic->id & 0xf) << 24);
65 break;
66 #endif
68 default:
69 {
70 uint32_t redir_index = (vioapic->ioregsel - 0x10) >> 1;
71 uint64_t redir_content;
73 if ( redir_index >= VIOAPIC_NUM_PINS )
74 {
75 gdprintk(XENLOG_WARNING, "apic_mem_readl:undefined ioregsel %x\n",
76 vioapic->ioregsel);
77 break;
78 }
80 redir_content = vioapic->redirtbl[redir_index].bits;
81 result = (vioapic->ioregsel & 0x1)?
82 (redir_content >> 32) & 0xffffffff :
83 redir_content & 0xffffffff;
84 break;
85 }
86 }
88 return result;
89 }
91 static int vioapic_read(
92 struct vcpu *v, unsigned long addr,
93 unsigned long length, unsigned long *pval)
94 {
95 struct hvm_hw_vioapic *vioapic = domain_vioapic(v->domain);
96 uint32_t result;
98 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "addr %lx", addr);
100 addr &= 0xff;
102 switch ( addr )
103 {
104 case VIOAPIC_REG_SELECT:
105 result = vioapic->ioregsel;
106 break;
108 case VIOAPIC_REG_WINDOW:
109 result = vioapic_read_indirect(vioapic, addr, length);
110 break;
112 default:
113 result = 0;
114 break;
115 }
117 *pval = result;
118 return X86EMUL_OKAY;
119 }
121 static void vioapic_write_redirent(
122 struct hvm_hw_vioapic *vioapic, unsigned int idx,
123 int top_word, uint32_t val)
124 {
125 struct domain *d = vioapic_domain(vioapic);
126 struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
127 union vioapic_redir_entry *pent, ent;
129 spin_lock(&d->arch.hvm_domain.irq_lock);
131 pent = &vioapic->redirtbl[idx];
132 ent = *pent;
134 if ( top_word )
135 {
136 /* Contains only the dest_id. */
137 ent.bits = (uint32_t)ent.bits | ((uint64_t)val << 32);
138 }
139 else
140 {
141 /* Remote IRR and Delivery Status are read-only. */
142 ent.bits = ((ent.bits >> 32) << 32) | val;
143 ent.fields.delivery_status = 0;
144 ent.fields.remote_irr = pent->fields.remote_irr;
145 }
147 *pent = ent;
149 if ( (ent.fields.trig_mode == VIOAPIC_LEVEL_TRIG) &&
150 !ent.fields.mask &&
151 !ent.fields.remote_irr &&
152 hvm_irq->gsi_assert_count[idx] )
153 {
154 pent->fields.remote_irr = 1;
155 vioapic_deliver(vioapic, idx);
156 }
158 spin_unlock(&d->arch.hvm_domain.irq_lock);
159 }
161 static void vioapic_write_indirect(
162 struct hvm_hw_vioapic *vioapic, unsigned long addr,
163 unsigned long length, unsigned long val)
164 {
165 switch ( vioapic->ioregsel )
166 {
167 case VIOAPIC_REG_VERSION:
168 /* Writes are ignored. */
169 break;
171 #if !VIOAPIC_IS_IOSAPIC
172 case VIOAPIC_REG_APIC_ID:
173 vioapic->id = (val >> 24) & 0xf;
174 break;
176 case VIOAPIC_REG_ARB_ID:
177 break;
178 #endif
180 default:
181 {
182 uint32_t redir_index = (vioapic->ioregsel - 0x10) >> 1;
184 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "change redir index %x val %lx",
185 redir_index, val);
187 if ( redir_index >= VIOAPIC_NUM_PINS )
188 {
189 gdprintk(XENLOG_WARNING, "vioapic_write_indirect "
190 "error register %x\n", vioapic->ioregsel);
191 break;
192 }
194 vioapic_write_redirent(
195 vioapic, redir_index, vioapic->ioregsel&1, val);
196 break;
197 }
198 }
199 }
201 static int vioapic_write(
202 struct vcpu *v, unsigned long addr,
203 unsigned long length, unsigned long val)
204 {
205 struct hvm_hw_vioapic *vioapic = domain_vioapic(v->domain);
207 addr &= 0xff;
209 switch ( addr )
210 {
211 case VIOAPIC_REG_SELECT:
212 vioapic->ioregsel = val;
213 break;
215 case VIOAPIC_REG_WINDOW:
216 vioapic_write_indirect(vioapic, addr, length, val);
217 break;
219 #if VIOAPIC_IS_IOSAPIC
220 case VIOAPIC_REG_EOI:
221 vioapic_update_EOI(v->domain, val);
222 break;
223 #endif
225 default:
226 break;
227 }
229 return X86EMUL_OKAY;
230 }
232 static int vioapic_range(struct vcpu *v, unsigned long addr)
233 {
234 struct hvm_hw_vioapic *vioapic = domain_vioapic(v->domain);
236 return ((addr >= vioapic->base_address &&
237 (addr < vioapic->base_address + VIOAPIC_MEM_LENGTH)));
238 }
240 struct hvm_mmio_handler vioapic_mmio_handler = {
241 .check_handler = vioapic_range,
242 .read_handler = vioapic_read,
243 .write_handler = vioapic_write
244 };
246 static void ioapic_inj_irq(
247 struct hvm_hw_vioapic *vioapic,
248 struct vlapic *target,
249 uint8_t vector,
250 uint8_t trig_mode,
251 uint8_t delivery_mode)
252 {
253 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "irq %d trig %d deliv %d",
254 vector, trig_mode, delivery_mode);
256 ASSERT((delivery_mode == dest_Fixed) ||
257 (delivery_mode == dest_LowestPrio));
259 if ( vlapic_set_irq(target, vector, trig_mode) )
260 vcpu_kick(vlapic_vcpu(target));
261 }
263 static uint32_t ioapic_get_delivery_bitmask(
264 struct hvm_hw_vioapic *vioapic, uint16_t dest, uint8_t dest_mode)
265 {
266 uint32_t mask = 0;
267 struct vcpu *v;
269 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "dest %d dest_mode %d",
270 dest, dest_mode);
272 if ( dest_mode == 0 ) /* Physical mode. */
273 {
274 if ( dest == 0xFF ) /* Broadcast. */
275 {
276 for_each_vcpu ( vioapic_domain(vioapic), v )
277 mask |= 1 << v->vcpu_id;
278 goto out;
279 }
281 for_each_vcpu ( vioapic_domain(vioapic), v )
282 {
283 if ( VLAPIC_ID(vcpu_vlapic(v)) == dest )
284 {
285 mask = 1 << v->vcpu_id;
286 break;
287 }
288 }
289 }
290 else if ( dest != 0 ) /* Logical mode, MDA non-zero. */
291 {
292 for_each_vcpu ( vioapic_domain(vioapic), v )
293 if ( vlapic_match_logical_addr(vcpu_vlapic(v), dest) )
294 mask |= 1 << v->vcpu_id;
295 }
297 out:
298 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "mask %x",
299 mask);
300 return mask;
301 }
303 static inline int pit_channel0_enabled(void)
304 {
305 PITState *pit = &current->domain->arch.hvm_domain.pl_time.vpit;
306 return pt_active(&pit->pt0);
307 }
309 static void vioapic_deliver(struct hvm_hw_vioapic *vioapic, int irq)
310 {
311 uint16_t dest = vioapic->redirtbl[irq].fields.dest_id;
312 uint8_t dest_mode = vioapic->redirtbl[irq].fields.dest_mode;
313 uint8_t delivery_mode = vioapic->redirtbl[irq].fields.delivery_mode;
314 uint8_t vector = vioapic->redirtbl[irq].fields.vector;
315 uint8_t trig_mode = vioapic->redirtbl[irq].fields.trig_mode;
316 uint32_t deliver_bitmask;
317 struct vlapic *target;
318 struct vcpu *v;
320 ASSERT(spin_is_locked(&vioapic_domain(vioapic)->arch.hvm_domain.irq_lock));
322 HVM_DBG_LOG(DBG_LEVEL_IOAPIC,
323 "dest=%x dest_mode=%x delivery_mode=%x "
324 "vector=%x trig_mode=%x",
325 dest, dest_mode, delivery_mode, vector, trig_mode);
327 deliver_bitmask = ioapic_get_delivery_bitmask(vioapic, dest, dest_mode);
328 if ( !deliver_bitmask )
329 {
330 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "no target on destination");
331 return;
332 }
334 switch ( delivery_mode )
335 {
336 case dest_LowestPrio:
337 {
338 #ifdef IRQ0_SPECIAL_ROUTING
339 /* Force round-robin to pick VCPU 0 */
340 if ( (irq == hvm_isa_irq_to_gsi(0)) && pit_channel0_enabled() )
341 {
342 v = vioapic_domain(vioapic)->vcpu ?
343 vioapic_domain(vioapic)->vcpu[0] : NULL;
344 target = v ? vcpu_vlapic(v) : NULL;
345 }
346 else
347 #endif
348 target = apic_lowest_prio(vioapic_domain(vioapic),
349 deliver_bitmask);
350 if ( target != NULL )
351 {
352 ioapic_inj_irq(vioapic, target, vector, trig_mode, delivery_mode);
353 }
354 else
355 {
356 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "null round robin: "
357 "mask=%x vector=%x delivery_mode=%x",
358 deliver_bitmask, vector, dest_LowestPrio);
359 }
360 break;
361 }
363 case dest_Fixed:
364 {
365 uint8_t bit;
366 for ( bit = 0; deliver_bitmask != 0; bit++ )
367 {
368 if ( !(deliver_bitmask & (1 << bit)) )
369 continue;
370 deliver_bitmask &= ~(1 << bit);
371 if ( vioapic_domain(vioapic)->vcpu == NULL )
372 v = NULL;
373 #ifdef IRQ0_SPECIAL_ROUTING
374 /* Do not deliver timer interrupts to VCPU != 0 */
375 else if ( (irq == hvm_isa_irq_to_gsi(0)) && pit_channel0_enabled() )
376 v = vioapic_domain(vioapic)->vcpu[0];
377 #endif
378 else
379 v = vioapic_domain(vioapic)->vcpu[bit];
380 if ( v != NULL )
381 {
382 target = vcpu_vlapic(v);
383 ioapic_inj_irq(vioapic, target, vector,
384 trig_mode, delivery_mode);
385 }
386 }
387 break;
388 }
390 case dest_NMI:
391 {
392 uint8_t bit;
393 for ( bit = 0; deliver_bitmask != 0; bit++ )
394 {
395 if ( !(deliver_bitmask & (1 << bit)) )
396 continue;
397 deliver_bitmask &= ~(1 << bit);
398 if ( (vioapic_domain(vioapic)->vcpu != NULL) &&
399 ((v = vioapic_domain(vioapic)->vcpu[bit]) != NULL) &&
400 !test_and_set_bool(v->nmi_pending) )
401 vcpu_kick(v);
402 }
403 break;
404 }
406 default:
407 gdprintk(XENLOG_WARNING, "Unsupported delivery mode %d\n",
408 delivery_mode);
409 break;
410 }
411 }
413 void vioapic_irq_positive_edge(struct domain *d, unsigned int irq)
414 {
415 struct hvm_hw_vioapic *vioapic = domain_vioapic(d);
416 union vioapic_redir_entry *ent;
418 HVM_DBG_LOG(DBG_LEVEL_IOAPIC, "irq %x", irq);
420 ASSERT(irq < VIOAPIC_NUM_PINS);
421 ASSERT(spin_is_locked(&d->arch.hvm_domain.irq_lock));
423 ent = &vioapic->redirtbl[irq];
424 if ( ent->fields.mask )
425 return;
427 if ( ent->fields.trig_mode == VIOAPIC_EDGE_TRIG )
428 {
429 vioapic_deliver(vioapic, irq);
430 }
431 else if ( !ent->fields.remote_irr )
432 {
433 ent->fields.remote_irr = 1;
434 vioapic_deliver(vioapic, irq);
435 }
436 }
438 static int get_eoi_gsi(struct hvm_hw_vioapic *vioapic, int vector)
439 {
440 int i;
442 for ( i = 0; i < VIOAPIC_NUM_PINS; i++ )
443 if ( vioapic->redirtbl[i].fields.vector == vector )
444 return i;
446 return -1;
447 }
449 void vioapic_update_EOI(struct domain *d, int vector)
450 {
451 struct hvm_hw_vioapic *vioapic = domain_vioapic(d);
452 struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
453 union vioapic_redir_entry *ent;
454 int gsi;
456 spin_lock(&d->arch.hvm_domain.irq_lock);
458 if ( (gsi = get_eoi_gsi(vioapic, vector)) == -1 )
459 {
460 gdprintk(XENLOG_WARNING, "Can't find redir item for %d EOI\n", vector);
461 goto out;
462 }
464 ent = &vioapic->redirtbl[gsi];
466 ent->fields.remote_irr = 0;
468 if ( iommu_enabled )
469 {
470 spin_unlock(&d->arch.hvm_domain.irq_lock);
471 hvm_dpci_eoi(current->domain, gsi, ent);
472 spin_lock(&d->arch.hvm_domain.irq_lock);
473 }
475 if ( (ent->fields.trig_mode == VIOAPIC_LEVEL_TRIG) &&
476 !ent->fields.mask &&
477 hvm_irq->gsi_assert_count[gsi] )
478 {
479 ent->fields.remote_irr = 1;
480 vioapic_deliver(vioapic, gsi);
481 }
483 out:
484 spin_unlock(&d->arch.hvm_domain.irq_lock);
485 }
487 static int ioapic_save(struct domain *d, hvm_domain_context_t *h)
488 {
489 struct hvm_hw_vioapic *s = domain_vioapic(d);
490 return hvm_save_entry(IOAPIC, 0, h, s);
491 }
493 static int ioapic_load(struct domain *d, hvm_domain_context_t *h)
494 {
495 struct hvm_hw_vioapic *s = domain_vioapic(d);
496 return hvm_load_entry(IOAPIC, h, s);
497 }
499 HVM_REGISTER_SAVE_RESTORE(IOAPIC, ioapic_save, ioapic_load, 1, HVMSR_PER_DOM);
501 void vioapic_reset(struct domain *d)
502 {
503 struct hvm_vioapic *vioapic = d->arch.hvm_domain.vioapic;
504 int i;
506 memset(&vioapic->hvm_hw_vioapic, 0, sizeof(vioapic->hvm_hw_vioapic));
507 for ( i = 0; i < VIOAPIC_NUM_PINS; i++ )
508 vioapic->hvm_hw_vioapic.redirtbl[i].fields.mask = 1;
509 vioapic->hvm_hw_vioapic.base_address = VIOAPIC_DEFAULT_BASE_ADDRESS;
510 }
512 int vioapic_init(struct domain *d)
513 {
514 if ( (d->arch.hvm_domain.vioapic == NULL) &&
515 ((d->arch.hvm_domain.vioapic = xmalloc(struct hvm_vioapic)) == NULL) )
516 return -ENOMEM;
518 d->arch.hvm_domain.vioapic->domain = d;
519 vioapic_reset(d);
521 return 0;
522 }
524 void vioapic_deinit(struct domain *d)
525 {
526 xfree(d->arch.hvm_domain.vioapic);
527 d->arch.hvm_domain.vioapic = NULL;
528 }