diff mbox series

[RFC,v2,12/21] ppc/xive: notify the CPU when interrupt priority is more privileged

Message ID 20170911171235.29331-13-clg@kaod.org
State New
Headers show
Series Guest exploitation of the XIVE interrupt controller (POWER9) | expand

Commit Message

Cédric Le Goater Sept. 11, 2017, 5:12 p.m. UTC
The Pending Interrupt Priority Register (PIPR) contains the priority
of the most favored pending notification. It is calculated from the
Interrupt Pending Buffer (IPB) which indicates a pending interrupt at
the priority corresponding to the bit number.

If the PIPR is more favored (1) than the Current Processor Priority
Register (CPPR), the CPU interrupt line can be raised and the EO bit
of the Notification Source Register is updated to notify the presence
of an exception for the O/S. The check needs to be done whenever the
PIPR or the CPPR is changed.

(1) numerically less than

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/intc/spapr_xive.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

Comments

David Gibson Sept. 19, 2017, 7:50 a.m. UTC | #1
On Mon, Sep 11, 2017 at 07:12:26PM +0200, Cédric Le Goater wrote:
> The Pending Interrupt Priority Register (PIPR) contains the priority
> of the most favored pending notification. It is calculated from the
> Interrupt Pending Buffer (IPB) which indicates a pending interrupt at
> the priority corresponding to the bit number.
> 
> If the PIPR is more favored (1) than the Current Processor Priority
> Register (CPPR), the CPU interrupt line can be raised and the EO bit
> of the Notification Source Register is updated to notify the presence
> of an exception for the O/S. The check needs to be done whenever the
> PIPR or the CPPR is changed.
> 
> (1) numerically less than
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/intc/spapr_xive.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 50 insertions(+)
> 
> diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c
> index 4bc61cfda67a..e5d4b723b7e0 100644
> --- a/hw/intc/spapr_xive.c
> +++ b/hw/intc/spapr_xive.c
> @@ -28,11 +28,39 @@
>  #include "xive-internal.h"
>  
>  
> +/* Convert a priority number to an Interrupt Pending Buffer (IPB)
> + * register, which indicates a pending interrupt at the priority
> + * corresponding to the bit number
> + */
> +static uint8_t priority_to_ipb(uint8_t priority)
> +{
> +    return priority > XIVE_PRIORITY_MAX ? 0 :  1 << (7 - priority);
> +}
> +
> +/* Convert an Interrupt Pending Buffer (IPB) register to a Pending
> + * Interrupt Priority Register (PIPR), which contains the priority of
> + * the most favored pending notification.
> + *
> + * TODO: PIPR can never be OxFF. Needs a fix.
> + */
> +static uint8_t ipb_to_pipr(uint8_t ibp)
> +{
> +    return ibp ? clz32((uint32_t)ibp << 24) : 0xff;
> +}
> +
>  static uint64_t spapr_xive_icp_accept(ICPState *icp)
>  {
>      return 0;
>  }
>  
> +static void spapr_xive_icp_notify(ICPState *icp)
> +{
> +    if (icp->tima_os[TM_PIPR] < icp->tima_os[TM_CPPR]) {
> +        icp->tima_os[TM_NSR] |= TM_QW1_NSR_EO;
> +        qemu_irq_raise(ICP(icp)->output);

The CPU interrupt lines are effectively level sensitive, but you never
lower this, AFAICT.

> +    }
> +}
> +
>  static void spapr_xive_icp_set_cppr(ICPState *icp, uint8_t cppr)
>  {
>      if (cppr > XIVE_PRIORITY_MAX) {
> @@ -40,6 +68,10 @@ static void spapr_xive_icp_set_cppr(ICPState *icp, uint8_t cppr)
>      }
>  
>      icp->tima_os[TM_CPPR] = cppr;
> +
> +    /* CPPR has changed, inform the ICP which might raise an
> +     * exception */
> +    spapr_xive_icp_notify(icp);
>  }
>  
>  /*
> @@ -206,6 +238,8 @@ static void spapr_xive_irq(sPAPRXive *xive, int srcno)
>      XiveEQ *eq;
>      uint32_t eq_idx;
>      uint32_t priority;
> +    uint32_t target;
> +    ICPState *icp;
>  
>      ive = spapr_xive_get_ive(xive, srcno);
>      if (!ive || !(ive->w & IVE_VALID)) {
> @@ -235,6 +269,13 @@ static void spapr_xive_irq(sPAPRXive *xive, int srcno)
>          qemu_log_mask(LOG_UNIMP, "XIVE: !UCOND_NOTIFY not implemented\n");
>      }
>  
> +    target = GETFIELD(EQ_W6_NVT_INDEX, eq->w6);
> +    icp = xics_icp_get(xive->ics->xics, target);
> +    if (!icp) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No ICP for target %d\n", target);
> +        return;
> +    }
> +
>      if (GETFIELD(EQ_W6_FORMAT_BIT, eq->w6) == 0) {
>          priority = GETFIELD(EQ_W7_F0_PRIORITY, eq->w7);
>  
> @@ -242,9 +283,18 @@ static void spapr_xive_irq(sPAPRXive *xive, int srcno)
>          if (priority == 0xff) {
>              return;
>          }
> +
> +        /* Update the IPB (Interrupt Pending Buffer) with the priority
> +         * of the new notification and inform the ICP, which will
> +         * decide to raise the exception, or not, depending the CPPR.
> +         */
> +        icp->tima_os[TM_IPB] |= priority_to_ipb(priority);
> +        icp->tima_os[TM_PIPR] = ipb_to_pipr(icp->tima_os[TM_IPB]);
>      } else {
>          qemu_log_mask(LOG_UNIMP, "XIVE: w7 format1 not implemented\n");
>      }
> +
> +    spapr_xive_icp_notify(icp);
>  }
>  
>  /*
diff mbox series

Patch

diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c
index 4bc61cfda67a..e5d4b723b7e0 100644
--- a/hw/intc/spapr_xive.c
+++ b/hw/intc/spapr_xive.c
@@ -28,11 +28,39 @@ 
 #include "xive-internal.h"
 
 
+/* Convert a priority number to an Interrupt Pending Buffer (IPB)
+ * register, which indicates a pending interrupt at the priority
+ * corresponding to the bit number
+ */
+static uint8_t priority_to_ipb(uint8_t priority)
+{
+    return priority > XIVE_PRIORITY_MAX ? 0 :  1 << (7 - priority);
+}
+
+/* Convert an Interrupt Pending Buffer (IPB) register to a Pending
+ * Interrupt Priority Register (PIPR), which contains the priority of
+ * the most favored pending notification.
+ *
+ * TODO: PIPR can never be OxFF. Needs a fix.
+ */
+static uint8_t ipb_to_pipr(uint8_t ibp)
+{
+    return ibp ? clz32((uint32_t)ibp << 24) : 0xff;
+}
+
 static uint64_t spapr_xive_icp_accept(ICPState *icp)
 {
     return 0;
 }
 
+static void spapr_xive_icp_notify(ICPState *icp)
+{
+    if (icp->tima_os[TM_PIPR] < icp->tima_os[TM_CPPR]) {
+        icp->tima_os[TM_NSR] |= TM_QW1_NSR_EO;
+        qemu_irq_raise(ICP(icp)->output);
+    }
+}
+
 static void spapr_xive_icp_set_cppr(ICPState *icp, uint8_t cppr)
 {
     if (cppr > XIVE_PRIORITY_MAX) {
@@ -40,6 +68,10 @@  static void spapr_xive_icp_set_cppr(ICPState *icp, uint8_t cppr)
     }
 
     icp->tima_os[TM_CPPR] = cppr;
+
+    /* CPPR has changed, inform the ICP which might raise an
+     * exception */
+    spapr_xive_icp_notify(icp);
 }
 
 /*
@@ -206,6 +238,8 @@  static void spapr_xive_irq(sPAPRXive *xive, int srcno)
     XiveEQ *eq;
     uint32_t eq_idx;
     uint32_t priority;
+    uint32_t target;
+    ICPState *icp;
 
     ive = spapr_xive_get_ive(xive, srcno);
     if (!ive || !(ive->w & IVE_VALID)) {
@@ -235,6 +269,13 @@  static void spapr_xive_irq(sPAPRXive *xive, int srcno)
         qemu_log_mask(LOG_UNIMP, "XIVE: !UCOND_NOTIFY not implemented\n");
     }
 
+    target = GETFIELD(EQ_W6_NVT_INDEX, eq->w6);
+    icp = xics_icp_get(xive->ics->xics, target);
+    if (!icp) {
+        qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No ICP for target %d\n", target);
+        return;
+    }
+
     if (GETFIELD(EQ_W6_FORMAT_BIT, eq->w6) == 0) {
         priority = GETFIELD(EQ_W7_F0_PRIORITY, eq->w7);
 
@@ -242,9 +283,18 @@  static void spapr_xive_irq(sPAPRXive *xive, int srcno)
         if (priority == 0xff) {
             return;
         }
+
+        /* Update the IPB (Interrupt Pending Buffer) with the priority
+         * of the new notification and inform the ICP, which will
+         * decide to raise the exception, or not, depending the CPPR.
+         */
+        icp->tima_os[TM_IPB] |= priority_to_ipb(priority);
+        icp->tima_os[TM_PIPR] = ipb_to_pipr(icp->tima_os[TM_IPB]);
     } else {
         qemu_log_mask(LOG_UNIMP, "XIVE: w7 format1 not implemented\n");
     }
+
+    spapr_xive_icp_notify(icp);
 }
 
 /*