]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
irqchip/gicv3-its: Don't allow devices whose ID is outside range
authorShanker Donthineni <shankerd@codeaurora.org>
Thu, 10 Mar 2016 04:10:49 +0000 (22:10 -0600)
committerMarc Zyngier <marc.zyngier@arm.com>
Wed, 11 May 2016 09:12:12 +0000 (10:12 +0100)
We are not checking whether the requested device identifier fits into
the device table memory or not. The function its_create_device()
assumes that enough memory has been allocated for whole DevID space
(reported by ITS_TYPER.Devbits) during the ITS probe() and continues
to initialize ITS hardware.

This assumption is not perfect, sometimes we reduce memory size either
because of its size crossing MAX_ORDER-1 or BASERn max size limit. The
MAPD command fails if 'Device ID' is outside of device table range.

Add a simple validation check to avoid MAPD failures since we are
not handling ITS command errors. This change also helps to return an
error -ENOMEM instead of success to caller.

Signed-off-by: Shanker Donthineni <shankerd@codeaurora.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
drivers/irqchip/irq-gic-v3-its.c

index 39261798c59f9d42f5a94d85d5a19d97c8a80d98..6bd881be24eafe2316f9cf137497c2f2dbef9109 100644 (file)
@@ -54,6 +54,16 @@ struct its_collection {
        u16                     col_id;
 };
 
+/*
+ * The ITS_BASER structure - contains memory information and cached
+ * value of BASER register configuration.
+ */
+struct its_baser {
+       void            *base;
+       u64             val;
+       u32             order;
+};
+
 /*
  * The ITS structure - contains most of the infrastructure, with the
  * top-level MSI domain, the command queue, the collections, and the
@@ -66,14 +76,12 @@ struct its_node {
        unsigned long           phys_base;
        struct its_cmd_block    *cmd_base;
        struct its_cmd_block    *cmd_write;
-       struct {
-               void            *base;
-               u32             order;
-       } tables[GITS_BASER_NR_REGS];
+       struct its_baser        tables[GITS_BASER_NR_REGS];
        struct its_collection   *collections;
        struct list_head        its_device_list;
        u64                     flags;
        u32                     ite_size;
+       u32                     device_ids;
 };
 
 #define ITS_ITT_ALIGN          SZ_256
@@ -838,6 +846,8 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
                ids     = GITS_TYPER_DEVBITS(typer);
        }
 
+       its->device_ids = ids;
+
        for (i = 0; i < GITS_BASER_NR_REGS; i++) {
                u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
                u64 type = GITS_BASER_TYPE(val);
@@ -913,6 +923,7 @@ retry_baser:
                }
 
                val |= alloc_pages - 1;
+               its->tables[i].val = val;
 
                writeq_relaxed(val, its->base + GITS_BASER + i * 8);
                tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
@@ -1138,9 +1149,22 @@ static struct its_device *its_find_device(struct its_node *its, u32 dev_id)
        return its_dev;
 }
 
+static struct its_baser *its_get_baser(struct its_node *its, u32 type)
+{
+       int i;
+
+       for (i = 0; i < GITS_BASER_NR_REGS; i++) {
+               if (GITS_BASER_TYPE(its->tables[i].val) == type)
+                       return &its->tables[i];
+       }
+
+       return NULL;
+}
+
 static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
                                            int nvecs)
 {
+       struct its_baser *baser;
        struct its_device *dev;
        unsigned long *lpi_map;
        unsigned long flags;
@@ -1151,6 +1175,16 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
        int nr_ites;
        int sz;
 
+       baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE);
+
+       /* Don't allow 'dev_id' that exceeds single, flat table limit */
+       if (baser) {
+               if (dev_id >= (PAGE_ORDER_TO_SIZE(baser->order) /
+                             GITS_BASER_ENTRY_SIZE(baser->val)))
+                       return NULL;
+       } else if (ilog2(dev_id) >= its->device_ids)
+               return NULL;
+
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        /*
         * At least one bit of EventID is being used, hence a minimum