Commit 54c96da798141feadd1424ef43fba19757e50d39

Authored by Michael S. Tsirkin
Committed by Anthony Liguori
1 parent 02eb84d0

qemu/apic: minimal MSI/MSI-X implementation for PC

Implement MSI support in APIC. Note that MSI and MMIO APIC registers
are at the same memory location, but actually not on the global bus: MSI
is on PCI bus, APIC is connected directly to the CPU. We map them on the
global bus at the same address which happens to work because MSI
registers are reserved in APIC MMIO and vice versa.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Showing 1 changed file with 39 additions and 4 deletions
hw/apic.c
@@ -19,6 +19,8 @@ @@ -19,6 +19,8 @@
19 */ 19 */
20 #include "hw.h" 20 #include "hw.h"
21 #include "pc.h" 21 #include "pc.h"
  22 +#include "pci.h"
  23 +#include "msix.h"
22 #include "qemu-timer.h" 24 #include "qemu-timer.h"
23 #include "host-utils.h" 25 #include "host-utils.h"
24 26
@@ -63,6 +65,19 @@ @@ -63,6 +65,19 @@
63 #define MAX_APICS 255 65 #define MAX_APICS 255
64 #define MAX_APIC_WORDS 8 66 #define MAX_APIC_WORDS 8
65 67
  68 +/* Intel APIC constants: from include/asm/msidef.h */
  69 +#define MSI_DATA_VECTOR_SHIFT 0
  70 +#define MSI_DATA_VECTOR_MASK 0x000000ff
  71 +#define MSI_DATA_DELIVERY_MODE_SHIFT 8
  72 +#define MSI_DATA_TRIGGER_SHIFT 15
  73 +#define MSI_DATA_LEVEL_SHIFT 14
  74 +#define MSI_ADDR_DEST_MODE_SHIFT 2
  75 +#define MSI_ADDR_DEST_ID_SHIFT 12
  76 +#define MSI_ADDR_DEST_ID_MASK 0x00ffff0
  77 +
  78 +#define MSI_ADDR_BASE 0xfee00000
  79 +#define MSI_ADDR_SIZE 0x100000
  80 +
66 typedef struct APICState { 81 typedef struct APICState {
67 CPUState *cpu_env; 82 CPUState *cpu_env;
68 uint32_t apicbase; 83 uint32_t apicbase;
@@ -745,11 +760,31 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) @@ -745,11 +760,31 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
745 return val; 760 return val;
746 } 761 }
747 762
  763 +static void apic_send_msi(target_phys_addr_t addr, uint32 data)
  764 +{
  765 + uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
  766 + uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
  767 + uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
  768 + uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
  769 + uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
  770 + /* XXX: Ignore redirection hint. */
  771 + apic_deliver_irq(dest, dest_mode, delivery, vector, 0, trigger_mode);
  772 +}
  773 +
748 static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) 774 static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
749 { 775 {
750 CPUState *env; 776 CPUState *env;
751 APICState *s; 777 APICState *s;
752 - int index; 778 + int index = (addr >> 4) & 0xff;
  779 + if (addr > 0xfff || !index) {
  780 + /* MSI and MMIO APIC are at the same memory location,
  781 + * but actually not on the global bus: MSI is on PCI bus
  782 + * APIC is connected directly to the CPU.
  783 + * Mapping them on the global bus happens to work because
  784 + * MSI registers are reserved in APIC MMIO and vice versa. */
  785 + apic_send_msi(addr, val);
  786 + return;
  787 + }
753 788
754 env = cpu_single_env; 789 env = cpu_single_env;
755 if (!env) 790 if (!env)
@@ -760,7 +795,6 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) @@ -760,7 +795,6 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
760 printf("APIC write: %08x = %08x\n", (uint32_t)addr, val); 795 printf("APIC write: %08x = %08x\n", (uint32_t)addr, val);
761 #endif 796 #endif
762 797
763 - index = (addr >> 4) & 0xff;  
764 switch(index) { 798 switch(index) {
765 case 0x02: 799 case 0x02:
766 s->id = (val >> 24); 800 s->id = (val >> 24);
@@ -946,6 +980,7 @@ int apic_init(CPUState *env) @@ -946,6 +980,7 @@ int apic_init(CPUState *env)
946 s->cpu_env = env; 980 s->cpu_env = env;
947 981
948 apic_reset(s); 982 apic_reset(s);
  983 + msix_supported = 1;
949 984
950 /* XXX: mapping more APICs at the same memory location */ 985 /* XXX: mapping more APICs at the same memory location */
951 if (apic_io_memory == 0) { 986 if (apic_io_memory == 0) {
@@ -953,7 +988,8 @@ int apic_init(CPUState *env) @@ -953,7 +988,8 @@ int apic_init(CPUState *env)
953 on the global memory bus. */ 988 on the global memory bus. */
954 apic_io_memory = cpu_register_io_memory(apic_mem_read, 989 apic_io_memory = cpu_register_io_memory(apic_mem_read,
955 apic_mem_write, NULL); 990 apic_mem_write, NULL);
956 - cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, 991 + /* XXX: what if the base changes? */
  992 + cpu_register_physical_memory(MSI_ADDR_BASE, MSI_ADDR_SIZE,
957 apic_io_memory); 993 apic_io_memory);
958 } 994 }
959 s->timer = qemu_new_timer(vm_clock, apic_timer, s); 995 s->timer = qemu_new_timer(vm_clock, apic_timer, s);
@@ -964,4 +1000,3 @@ int apic_init(CPUState *env) @@ -964,4 +1000,3 @@ int apic_init(CPUState *env)
964 local_apics[s->idx] = s; 1000 local_apics[s->idx] = s;
965 return 0; 1001 return 0;
966 } 1002 }
967 -