diff mbox

[2/4] intel_iommu: use deliver_msi APIC callback

Message ID 1462568028-31037-3-git-send-email-rkrcmar@redhat.com
State New
Headers show

Commit Message

Radim Krčmář May 6, 2016, 8:53 p.m. UTC
The memory-mapped interface cannot express x2APIC destinations that are
a result of remapping.

Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
---
 hw/i386/intel_iommu.c | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

Comments

Eduardo Habkost May 13, 2016, 5:33 p.m. UTC | #1
On Fri, May 06, 2016 at 10:53:46PM +0200, Radim Krčmář wrote:
> The memory-mapped interface cannot express x2APIC destinations that are
> a result of remapping.
> 
> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
> ---
>  hw/i386/intel_iommu.c | 29 ++++++++++++++++++-----------
>  1 file changed, 18 insertions(+), 11 deletions(-)
> 
> diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
> index bee85e469477..d10064289551 100644
> --- a/hw/i386/intel_iommu.c
> +++ b/hw/i386/intel_iommu.c
> @@ -26,6 +26,7 @@
>  #include "hw/pci/pci.h"
>  #include "hw/boards.h"
>  #include "hw/i386/x86-iommu.h"
> +#include "hw/i386/apic_internal.h"
>  
>  /*#define DEBUG_INTEL_IOMMU*/
>  #ifdef DEBUG_INTEL_IOMMU
> @@ -268,24 +269,33 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
>      g_hash_table_replace(s->iotlb, key, entry);
>  }
>  
> +static void apic_deliver_msi(MSIMessage *msi)
> +{
> +    /* Conjure apic-bound msi delivery out of thin air. */
> +    X86CPU *cpu = X86_CPU(first_cpu);
> +    APICCommonState *apic_state = APIC_COMMON(cpu->apic_state);
> +    APICCommonClass *apic_class = APIC_COMMON_GET_CLASS(apic_state);

I see a pattern here:

hw/i386/kvmvapic.c-static void do_vapic_enable(void *data)
hw/i386/kvmvapic.c-{
[...]
hw/i386/kvmvapic.c-    X86CPU *cpu = X86_CPU(first_cpu);
[...]
hw/i386/kvmvapic.c:    apic_enable_vapic(cpu->apic_state, s->vapic_paddr);
[...]

hw/i386/pc.c-static void pic_irq_request(void *opaque, int irq, int level)
hw/i386/pc.c-{
hw/i386/pc.c-    CPUState *cs = first_cpu;
hw/i386/pc.c-    X86CPU *cpu = X86_CPU(cs);
[...]
hw/i386/pc.c:    if (cpu->apic_state) {
[...]


Time to write a common pc_get_first_apic() helper, or provide a
PCMachineState::first_apic field?
Paolo Bonzini May 17, 2016, 1:09 p.m. UTC | #2
On 13/05/2016 19:33, Eduardo Habkost wrote:
> On Fri, May 06, 2016 at 10:53:46PM +0200, Radim Krčmář wrote:
>> The memory-mapped interface cannot express x2APIC destinations that are
>> a result of remapping.
>>
>> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
>> ---
>>  hw/i386/intel_iommu.c | 29 ++++++++++++++++++-----------
>>  1 file changed, 18 insertions(+), 11 deletions(-)
>>
>> diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
>> index bee85e469477..d10064289551 100644
>> --- a/hw/i386/intel_iommu.c
>> +++ b/hw/i386/intel_iommu.c
>> @@ -26,6 +26,7 @@
>>  #include "hw/pci/pci.h"
>>  #include "hw/boards.h"
>>  #include "hw/i386/x86-iommu.h"
>> +#include "hw/i386/apic_internal.h"
>>  
>>  /*#define DEBUG_INTEL_IOMMU*/
>>  #ifdef DEBUG_INTEL_IOMMU
>> @@ -268,24 +269,33 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
>>      g_hash_table_replace(s->iotlb, key, entry);
>>  }
>>  
>> +static void apic_deliver_msi(MSIMessage *msi)
>> +{
>> +    /* Conjure apic-bound msi delivery out of thin air. */
>> +    X86CPU *cpu = X86_CPU(first_cpu);
>> +    APICCommonState *apic_state = APIC_COMMON(cpu->apic_state);
>> +    APICCommonClass *apic_class = APIC_COMMON_GET_CLASS(apic_state);
> 
> I see a pattern here:
> 
> hw/i386/kvmvapic.c-static void do_vapic_enable(void *data)
> hw/i386/kvmvapic.c-{
> [...]
> hw/i386/kvmvapic.c-    X86CPU *cpu = X86_CPU(first_cpu);
> [...]
> hw/i386/kvmvapic.c:    apic_enable_vapic(cpu->apic_state, s->vapic_paddr);
> [...]

Here first_cpu is just the CPU that do_vapic_enable is being called on
(via run_on_cpu).  So this one can use the posted patch that passes a
CPUState* to run_on_cpu callbacks.


> hw/i386/pc.c-static void pic_irq_request(void *opaque, int irq, int level)
> hw/i386/pc.c-{
> hw/i386/pc.c-    CPUState *cs = first_cpu;
> hw/i386/pc.c-    X86CPU *cpu = X86_CPU(cs);
> [...]
> hw/i386/pc.c:    if (cpu->apic_state) {
> [...]
> 
> 
> Time to write a common pc_get_first_apic() helper, or provide a
> PCMachineState::first_apic field?

For this one, we could add a pc_get_apic_class() helper that returns
NULL if there is no APIC on the first_cpu.  It wouldn't be a change for
this code and would be nicer for Radim's use case.

Paolo
Eduardo Habkost May 17, 2016, 1:53 p.m. UTC | #3
On Tue, May 17, 2016 at 03:09:49PM +0200, Paolo Bonzini wrote:
> 
> 
> On 13/05/2016 19:33, Eduardo Habkost wrote:
> > On Fri, May 06, 2016 at 10:53:46PM +0200, Radim Krčmář wrote:
> >> The memory-mapped interface cannot express x2APIC destinations that are
> >> a result of remapping.
> >>
> >> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
> >> ---
> >>  hw/i386/intel_iommu.c | 29 ++++++++++++++++++-----------
> >>  1 file changed, 18 insertions(+), 11 deletions(-)
> >>
> >> diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
> >> index bee85e469477..d10064289551 100644
> >> --- a/hw/i386/intel_iommu.c
> >> +++ b/hw/i386/intel_iommu.c
> >> @@ -26,6 +26,7 @@
> >>  #include "hw/pci/pci.h"
> >>  #include "hw/boards.h"
> >>  #include "hw/i386/x86-iommu.h"
> >> +#include "hw/i386/apic_internal.h"
> >>  
> >>  /*#define DEBUG_INTEL_IOMMU*/
> >>  #ifdef DEBUG_INTEL_IOMMU
> >> @@ -268,24 +269,33 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
> >>      g_hash_table_replace(s->iotlb, key, entry);
> >>  }
> >>  
> >> +static void apic_deliver_msi(MSIMessage *msi)
> >> +{
> >> +    /* Conjure apic-bound msi delivery out of thin air. */
> >> +    X86CPU *cpu = X86_CPU(first_cpu);
> >> +    APICCommonState *apic_state = APIC_COMMON(cpu->apic_state);
> >> +    APICCommonClass *apic_class = APIC_COMMON_GET_CLASS(apic_state);
> > 
> > I see a pattern here:
> > 
> > hw/i386/kvmvapic.c-static void do_vapic_enable(void *data)
> > hw/i386/kvmvapic.c-{
> > [...]
> > hw/i386/kvmvapic.c-    X86CPU *cpu = X86_CPU(first_cpu);
> > [...]
> > hw/i386/kvmvapic.c:    apic_enable_vapic(cpu->apic_state, s->vapic_paddr);
> > [...]
> 
> Here first_cpu is just the CPU that do_vapic_enable is being called on
> (via run_on_cpu).  So this one can use the posted patch that passes a
> CPUState* to run_on_cpu callbacks.
> 
> 
> > hw/i386/pc.c-static void pic_irq_request(void *opaque, int irq, int level)
> > hw/i386/pc.c-{
> > hw/i386/pc.c-    CPUState *cs = first_cpu;
> > hw/i386/pc.c-    X86CPU *cpu = X86_CPU(cs);
> > [...]
> > hw/i386/pc.c:    if (cpu->apic_state) {
> > [...]
> > 
> > 
> > Time to write a common pc_get_first_apic() helper, or provide a
> > PCMachineState::first_apic field?
> 
> For this one, we could add a pc_get_apic_class() helper that returns
> NULL if there is no APIC on the first_cpu.  It wouldn't be a change for
> this code and would be nicer for Radim's use case.

Returning just the APIC class would be even nicer, yes. We could
just move the type name logic in x86_cpu_apic_create() to
pc_get_apic_class().

About returning NULL if there is no APIC on first_cpu: I would
like to avoid making more code depend on first_cpu if possible.
pc_get_first_apic() wouldn't even need to use
first_cpu/pc_get_apic_class() if we just do this:

    CPU_FOREACH(cs) {
        cpu = X86_CPU(cs);
        if (!cpu->apic_state) {
            if (level) {
                cpu_interrupt(cs, CPU_INTERRUPT_HARD);
            } else {
                cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
            }
            break;
        }
        if (apic_accept_pic_intr(cpu->apic_state)) {
            apic_deliver_pic_intr(cpu->apic_state, level);
        }
    }
Paolo Bonzini May 17, 2016, 4:43 p.m. UTC | #4
On 17/05/2016 15:53, Eduardo Habkost wrote:
> On Tue, May 17, 2016 at 03:09:49PM +0200, Paolo Bonzini wrote:
>>
>>
>> On 13/05/2016 19:33, Eduardo Habkost wrote:
>>> On Fri, May 06, 2016 at 10:53:46PM +0200, Radim Krčmář wrote:
>>>> The memory-mapped interface cannot express x2APIC destinations that are
>>>> a result of remapping.
>>>>
>>>> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
>>>> ---
>>>>  hw/i386/intel_iommu.c | 29 ++++++++++++++++++-----------
>>>>  1 file changed, 18 insertions(+), 11 deletions(-)
>>>>
>>>> diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
>>>> index bee85e469477..d10064289551 100644
>>>> --- a/hw/i386/intel_iommu.c
>>>> +++ b/hw/i386/intel_iommu.c
>>>> @@ -26,6 +26,7 @@
>>>>  #include "hw/pci/pci.h"
>>>>  #include "hw/boards.h"
>>>>  #include "hw/i386/x86-iommu.h"
>>>> +#include "hw/i386/apic_internal.h"
>>>>  
>>>>  /*#define DEBUG_INTEL_IOMMU*/
>>>>  #ifdef DEBUG_INTEL_IOMMU
>>>> @@ -268,24 +269,33 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
>>>>      g_hash_table_replace(s->iotlb, key, entry);
>>>>  }
>>>>  
>>>> +static void apic_deliver_msi(MSIMessage *msi)
>>>> +{
>>>> +    /* Conjure apic-bound msi delivery out of thin air. */
>>>> +    X86CPU *cpu = X86_CPU(first_cpu);
>>>> +    APICCommonState *apic_state = APIC_COMMON(cpu->apic_state);
>>>> +    APICCommonClass *apic_class = APIC_COMMON_GET_CLASS(apic_state);
>>>
>>> I see a pattern here:
>>>
>>> hw/i386/kvmvapic.c-static void do_vapic_enable(void *data)
>>> hw/i386/kvmvapic.c-{
>>> [...]
>>> hw/i386/kvmvapic.c-    X86CPU *cpu = X86_CPU(first_cpu);
>>> [...]
>>> hw/i386/kvmvapic.c:    apic_enable_vapic(cpu->apic_state, s->vapic_paddr);
>>> [...]
>>
>> Here first_cpu is just the CPU that do_vapic_enable is being called on
>> (via run_on_cpu).  So this one can use the posted patch that passes a
>> CPUState* to run_on_cpu callbacks.
>>
>>
>>> hw/i386/pc.c-static void pic_irq_request(void *opaque, int irq, int level)
>>> hw/i386/pc.c-{
>>> hw/i386/pc.c-    CPUState *cs = first_cpu;
>>> hw/i386/pc.c-    X86CPU *cpu = X86_CPU(cs);
>>> [...]
>>> hw/i386/pc.c:    if (cpu->apic_state) {
>>> [...]
>>>
>>>
>>> Time to write a common pc_get_first_apic() helper, or provide a
>>> PCMachineState::first_apic field?
>>
>> For this one, we could add a pc_get_apic_class() helper that returns
>> NULL if there is no APIC on the first_cpu.  It wouldn't be a change for
>> this code and would be nicer for Radim's use case.
> 
> Returning just the APIC class would be even nicer, yes. We could
> just move the type name logic in x86_cpu_apic_create() to
> pc_get_apic_class().
> 
> About returning NULL if there is no APIC on first_cpu: I would
> like to avoid making more code depend on first_cpu if possible.
> pc_get_first_apic() wouldn't even need to use
> first_cpu/pc_get_apic_class() if we just do this:
> 
>     CPU_FOREACH(cs) {
>         cpu = X86_CPU(cs);
>         if (!cpu->apic_state) {
>             if (level) {
>                 cpu_interrupt(cs, CPU_INTERRUPT_HARD);
>             } else {
>                 cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
>             }
>             break;
>         }
>         if (apic_accept_pic_intr(cpu->apic_state)) {
>             apic_deliver_pic_intr(cpu->apic_state, level);
>         }
>     }
> 

Sounds good.

Paolo
diff mbox

Patch

diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index bee85e469477..d10064289551 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -26,6 +26,7 @@ 
 #include "hw/pci/pci.h"
 #include "hw/boards.h"
 #include "hw/i386/x86-iommu.h"
+#include "hw/i386/apic_internal.h"
 
 /*#define DEBUG_INTEL_IOMMU*/
 #ifdef DEBUG_INTEL_IOMMU
@@ -268,24 +269,33 @@  static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
     g_hash_table_replace(s->iotlb, key, entry);
 }
 
+static void apic_deliver_msi(MSIMessage *msi)
+{
+    /* Conjure apic-bound msi delivery out of thin air. */
+    X86CPU *cpu = X86_CPU(first_cpu);
+    APICCommonState *apic_state = APIC_COMMON(cpu->apic_state);
+    APICCommonClass *apic_class = APIC_COMMON_GET_CLASS(apic_state);
+
+    apic_class->deliver_msi(msi);
+}
+
 /* Given the reg addr of both the message data and address, generate an
  * interrupt via MSI.
  */
 static void vtd_generate_interrupt(IntelIOMMUState *s, hwaddr mesg_addr_reg,
                                    hwaddr mesg_data_reg)
 {
-    hwaddr addr;
-    uint32_t data;
+    MSIMessage msi;
 
     assert(mesg_data_reg < DMAR_REG_SIZE);
     assert(mesg_addr_reg < DMAR_REG_SIZE);
 
-    addr = vtd_get_long_raw(s, mesg_addr_reg);
-    data = vtd_get_long_raw(s, mesg_data_reg);
+    msi.address = vtd_get_quad_raw(s, mesg_addr_reg);
+    msi.data = vtd_get_long_raw(s, mesg_data_reg);
 
     VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32, addr, data);
-    address_space_stl_le(&address_space_memory, addr, data,
-                         MEMTXATTRS_UNSPECIFIED, NULL);
+
+    apic_deliver_msi(&msi);
 }
 
 /* Generate a fault event to software via MSI if conditions are met.
@@ -2113,6 +2123,7 @@  static void vtd_generate_msi_message(VTDIrq *irq, MSIMessage *msg_out)
     msg.dest_mode = irq->dest_mode;
     msg.redir_hint = irq->redir_hint;
     msg.dest = irq->dest;
+    msg.__addr_hi = irq->dest & 0xffffff00;
     msg.__addr_head = 0xfee;
     /* Keep this from original MSI address bits */
     msg.__not_used = irq->msi_addr_last_bits;
@@ -2262,11 +2273,7 @@  static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr,
     VTD_DPRINTF(IR, "delivering MSI 0x%"PRIx64":0x%"PRIx32,
                 to.address, to.data);
 
-    if (dma_memory_write(&address_space_memory, to.address,
-                         &to.data, size)) {
-        VTD_DPRINTF(GENERAL, "error: fail to write 0x%"PRIx64
-                    " value 0x%"PRIx32, to.address, to.data);
-    }
+    apic_deliver_msi(&to);
 
     return MEMTX_OK;
 }