[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [rfc 16/18] ioemu: non-destructive parsing of PCI assignement strings



Signed-off-by: Simon Horman <horms@xxxxxxxxxxxx>

--- 
Fri, 13 Feb 2009 13:45:22 +1100
* Fix parsing of vslot - the regex was incorrect
* Allow 0x to prefix hex values that are parsed

Fri, 13 Feb 2009 15:22:10 +1100
* Rebased
  from "Restore xenfb.h and atkbd_ translation tables for * xenfbfront"
  to "fix memory/fd leak in pt_msix_init()"
Index: ioemu-remote/hw/pass-through.c
===================================================================
--- ioemu-remote.orig/hw/pass-through.c 2009-02-17 17:56:46.000000000 +0900
+++ ioemu-remote/hw/pass-through.c      2009-02-17 17:57:25.000000000 +0900
@@ -28,6 +28,12 @@
 #include "pt-msi.h"
 #include "qemu-xen.h"
 
+#include <sys/types.h>
+#include <regex.h>
+
+#define PHP_DEV_OPT_MSITRANSLATE 0x01
+#define PHP_DEV_OPT_ERROR        0x80
+
 struct php_dev {
     struct pt_dev *pt_dev;
     uint8_t valid;
@@ -35,7 +41,7 @@ struct php_dev {
     uint8_t r_dev;
     uint8_t r_func;
     uint8_t v_devfn;
-    char *opt;
+    uint8_t opt;
 };
 static struct dpci_infos {
 
@@ -750,62 +756,199 @@ static void cpy_php_dev(struct php_dev *
     memcpy(dst, src, sizeof(*dst));
 }
 
-static int token_value(char *token)
+static char *regerror_log(int errcode, const regex_t *preg)
 {
-    return strtol(token, NULL, 16);
+    size_t len = 0, new_len;
+    char *buf = NULL;
+
+    while (1)
+    {
+        new_len = regerror(errcode, preg, buf, len);
+        if (new_len <= len)
+            break;
+        len = new_len;
+        if (!(buf = realloc(buf, len)))
+        {
+            PT_LOG("Error: can't allocate %lu bytes for rexex error message\n",
+                   len);
+            return NULL;
+        }
+    }
+
+    return buf;
 }
 
-static struct php_dev next_bdf(char **str)
+static int opt_cmp(const char *source, const char *match)
 {
-    char *token;
-    const char *delim = ":.-";
-    struct php_dev bdf;
+    return strncmp(source, match, strlen(match));
+}
 
-    memset(&bdf, 0, sizeof(bdf));
+#define RE_OPT_MSITRANSLATE "msitranslate=\\(yes\\|1\\|no\\|0\\)"
 
-    if ( !(*str) ||
-          ( !strchr(*str, ':') && !strchr(*str, '.')) )
-        return bdf;
+static uint8_t parse_opt(const char *str, size_t n, int8_t opt)
+{
+    regmatch_t pmatch[2];
+    regex_t preg;
+    int err, status = PHP_DEV_OPT_ERROR;
+    char *err_str;
 
-    token  = strsep(str, delim);
-    /* segment */
+    if ((err = regcomp(&preg, RE_OPT_MSITRANSLATE, 0)))
+    {
+        if ((err_str = regerror_log(err, &preg)))
+        {
+            PT_LOG("regcomp() failed: %s\n", err_str);
+        }
+        else
+        {
+            PT_LOG("regcomp() failed\n");
+        }
+        return PHP_DEV_OPT_ERROR;
+    }
+
+    while (n > 0)
+    {
+        if ((err = regexec(&preg, str, 2, pmatch, 0)))
+        {
+            PT_LOG("Error: unrecognized PCI assignment option at \"%s\"\n",
+                   str);
+            goto err;
+        }
 
-    token  = strsep(str, delim);
-    bdf.r_bus  = token_value(token);
+        if (!opt_cmp(str + pmatch[1].rm_so, "no") ||
+            !opt_cmp(str + pmatch[1].rm_so, "0"))
+        {
+            opt &= ~PHP_DEV_OPT_MSITRANSLATE;
+        }
+        else if (!opt_cmp(str + pmatch[1].rm_so, "yes") ||
+                 !opt_cmp(str + pmatch[1].rm_so, "1"))
+        {
+            opt |= PHP_DEV_OPT_MSITRANSLATE;
+        }
 
-    token  = strsep(str, delim);
-    bdf.r_dev  = token_value(token);
+        n -= pmatch[0].rm_eo;
+        str += pmatch[0].rm_eo;
+        if (!n)
+        {
+            break;
+        }
+        if (*str != ',')
+        {
+            PT_LOG("Error: trailing garbage in PCI assignment option at "
+                   "\"%s\"\n", str);
+            goto err;
+        }
+        n--;
+        str++;
+        if (!n)
+        {
+            break;
+        }
+    }
 
-    token  = strsep(str, delim);
-    bdf.opt = strchr(token, ',');
-    if (bdf.opt)
-        *(bdf.opt)++ = '\0';
+    status = opt;
+err:
+    regfree(&preg);
+    return status;
+}
 
-    bdf.r_func  = token_value(token);
+#define RE_SEG  "\\(0x\\)\\?[0-9a-fA-F]\\{4\\}"
+#define RE_BUS  "\\(0x\\)\\?\\([0-9a-fA-F]\\{2\\}\\)"
+#define RE_DEV  "\\(0x\\)\\?\\([01][0-9a-fA-F]\\)"
+#define RE_FUNC "\\(0x\\)\\?\\([0-7]\\)"
+#define RE_BDF  RE_SEG ":" RE_BUS ":" RE_DEV "\\." RE_FUNC
 
-    bdf.valid = 1;
+#define RE_OPT  "\\(,\\([^@-]\\+\\)\\)"
+#define RE_BDF_OPT RE_BDF RE_OPT "\\?"
 
-    return bdf;
-}
+#define RE_VDEV  "\\(0x\\)\\?\\([01]\\?[0-9a-fA-F]\\)"
+#define RE_BDF_OPT_SLOT RE_BDF_OPT "\\(@" RE_VDEV "\\)\\?"
 
-static int get_next_keyval(char **option, char **key, char **val)
+static struct php_dev *parse_bdf(const char *str)
 {
-    char *opt, *k, *v;
+    struct php_dev *list = NULL, *e;
+    regex_t preg;
+    regmatch_t pmatch[14];
+    int err, nmemb = 0;
+    char *err_str;
 
-    k = *option;
-    opt = strchr(k, ',');
-    if (opt)
-        *opt++ = '\0';
-    v = strchr(k, '=');
-    if (!v)
-        return -1;
-    *v++ = '\0';
+    if ((err = regcomp(&preg, RE_BDF_OPT_SLOT, 0)))
+    {
+        if ((err_str = regerror_log(err, &preg)))
+        {
+            PT_LOG("regcomp() failed: %s\n", err_str);
+        }
+        else
+        {
+            PT_LOG("regcomp() failed\n");
+        }
+        return NULL;
+    }
 
-    *key = k;
-    *val = v;
-    *option = opt;
+    while (1)
+    {
+        if ((err = regexec(&preg, str, 14, pmatch, 0)))
+        {
+            PT_LOG("Error: invalid PCI assignment \"%s\"\n", str);
+            goto err;
+        }
 
-    return 0;
+        list = realloc(list, (++nmemb + 1) * sizeof(*list));
+        e = list + nmemb - 1;
+        memset(e, 0, sizeof(*e) * 2);
+
+        e->r_bus = strtol(str + pmatch[3].rm_so, NULL, 16);
+        e->r_dev = strtol(str + pmatch[5].rm_so, NULL, 16);
+        e->r_func = strtol(str + pmatch[7].rm_so, NULL, 16);
+
+        if (pmatch[9].rm_so >= 0)
+        {
+           if (parse_opt(str + pmatch[9].rm_so,
+                         pmatch[9].rm_eo - pmatch[9].rm_so,
+                         direct_pci_msitranslate) & PHP_DEV_OPT_ERROR)
+           {
+               goto err;
+           }
+        }
+
+        if (pmatch[12].rm_so >= 0)
+        {
+            e->v_devfn = PCI_DEVFN(strtol(str + pmatch[12].rm_so, NULL, 16), 
0);
+        }
+
+        e->valid = 1;
+
+        str += pmatch[0].rm_eo;
+        if (!*str)
+        {
+            break;
+        }
+        if (*str != '-')
+        {
+            PT_LOG("Error: trailing garbage in PCI assignment at \"%s\"\n",
+                   str);
+            goto err;
+        }
+        str++;
+        /* A trailing '-' delimiter is ok */
+        if (!*str)
+        {
+            break;
+        }
+    }
+
+    regfree(&preg);
+    return list;
+
+err:
+    regfree(&preg);
+    if (list)
+        free(list);
+    return NULL;
+}
+
+static struct php_dev *next_bdf(struct php_dev *l)
+{
+    return (++l)->valid ? l : NULL;
 }
 
 static void msi_set_enable(struct pt_dev *ptdev, int en)
@@ -881,27 +1024,21 @@ found:
 /* Insert a new pass-through device into function 0 of a specific pci slot.
  * input  dom:bus:dev.func@slot
  */
-int insert_bdf_to_php_devfn(char *bdf_slt)
+int insert_bdf_to_php_devfn(const char *bdf_slt)
 {
-    int slot;
-    struct php_dev bdf;
-    char *bdf_str, *slt_str, *opt;
-    const char *delim="@";
-
-    bdf_str = strsep(&bdf_slt, delim);
-    slt_str = bdf_slt;
-    slot = token_value(slt_str);
+    struct php_dev *bdf;
+    int status;
 
-    bdf = next_bdf(&bdf_str);
-    if (!bdf.valid)
+    bdf = parse_bdf(bdf_slt);
+    if (!bdf)
     {
         return -1;
     }
 
-    bdf.v_devfn = PCI_DEVFN(slot, 0);
-
-    return insert_to_php_devfn(&bdf);
+    status = insert_to_php_devfn(bdf);
+    free(bdf);
 
+    return status;
 }
 
 /* Test if a pci devfn has a device
@@ -924,30 +1061,32 @@ int test_php_devfn(int devfn)
 }
 
 /* find the pci slot for pass-through dev with specified BDF */
-int bdf_to_php_devfn(char *bdf_str)
+int bdf_to_php_devfn(const char *bdf_str)
 {
-    int i;
-    struct php_dev bdf;
+    int i, status = -1;
+    struct php_dev *bdf;
 
-    bdf = next_bdf(&bdf_str);
-    if (!bdf.valid)
+    bdf = parse_bdf(bdf_str);
+    if (!bdf)
     {
         return -1;
     }
-    PT_LOG("%s: bdf: %04x:%02x.%x\n", __func__, bdf.r_bus, bdf.r_dev,
-           bdf.r_func);
+    PT_LOG("%s: bdf: %04x:%02x.%x\n", __func__, bdf->r_bus, bdf->r_dev,
+           bdf->r_func);
 
     /* locate the virtual pci slot for this VTd device */
     for ( i = 0; i < PHP_DEVFN_LEN; i++ )
     {
         if ( dpci_infos.php_devs[i].valid &&
-             cmp_php_dev(&dpci_infos.php_devs[i], &bdf) )
+             cmp_php_dev(&dpci_infos.php_devs[i], bdf) )
         {
-            return PHP_TO_PCI_DEVFN(i);
+            status = PHP_TO_PCI_DEVFN(i);
+            break;
         }
     }
 
-    return -1;
+    free(bdf);
+    return status;
 }
 
 /* Being called each time a mmio region has been updated */
@@ -3132,8 +3271,6 @@ static struct pt_dev * register_real_dev
     uint8_t e_device, e_intx;
     struct pci_config_cf8 machine_bdf;
     int free_devfn = -1;
-    char *key, *val;
-    int msi_translate;
 
     PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
         bdf->r_bus, bdf->r_dev, bdf->r_func);
@@ -3159,35 +3296,6 @@ static struct pt_dev * register_real_dev
         PT_LOG("Error: no free virtual PCI hot plug slot, "
                "thus no live migration.\n");
 
-    msi_translate = direct_pci_msitranslate;
-    while (bdf->opt) {
-        if (get_next_keyval(&bdf->opt, &key, &val)) {
-            PT_LOG("Error: unrecognized PCI assignment option \"%s\"\n",
-                   bdf->opt);
-            break;
-        }
-
-        if (strcmp(key, "msitranslate") == 0)
-        {
-            if (strcmp(val, "0") == 0 || strcmp(val, "no") == 0)
-            {
-                PT_LOG("Disable MSI translation via per device option\n");
-                msi_translate = 0;
-            }
-            else if (strcmp(val, "1") == 0 || strcmp(val, "yes") == 0)
-            {
-                PT_LOG("Enable MSI translation via per device option\n");
-                msi_translate = 1;
-            }
-            else
-                PT_LOG("Error: unrecognized value for msitranslate=\n");
-        }
-        else
-            PT_LOG("Error: unrecognized PCI assignment option \"%s=%s\"\n", 
key, val);
-
-    }
-
-
     /* Register device */
     assigned_device = (struct pt_dev *) pci_register_device(e_bus, e_dev_name,
                                 sizeof(struct pt_dev), free_devfn,
@@ -3203,7 +3311,10 @@ static struct pt_dev * register_real_dev
                 assigned_device;
 
     assigned_device->pci_dev = pci_dev;
-    assigned_device->msi_trans_cap = msi_translate;
+    if (bdf->opt & PHP_DEV_OPT_MSITRANSLATE)
+    {
+        assigned_device->msi_trans_cap = 1;
+    }
 
     /* Assign device */
     machine_bdf.reg = 0;
@@ -3384,9 +3495,6 @@ int power_on_php_devfn(int php_devfn)
     pt_dev = register_real_device(dpci_infos.e_bus, "DIRECT PCI",
                                   php_dev, PT_MACHINE_IRQ_AUTO,
                                   dpci_infos.pci_access);
-
-    php_dev->opt = NULL;
-
     php_dev->pt_dev = pt_dev;
 
     return 0;
@@ -3405,9 +3513,7 @@ int pt_init(PCIBus *e_bus, const char *d
     struct pci_access *pci_access;
     char *vslots;
     char slot_str[8];
-    char *direct_pci_head = NULL;
-    char *direct_pci_p = NULL;
-    struct php_dev bdf;
+    struct php_dev *list, *bdf;
 
     /* Initialize libpci */
     pci_access = pci_alloc();
@@ -3427,29 +3533,21 @@ int pt_init(PCIBus *e_bus, const char *d
         return 0;
     }
 
-    if ( !(direct_pci_head = direct_pci_p = strdup(direct_pci)) )
-        return 0;
-
     /* the virtual pci slots of all pass-through devs
      * with hex format: xx;xx...;
      */
     vslots = qemu_mallocz ( strlen(direct_pci) / 3 );
 
     /* Assign given devices to guest */
-    while (1)
+    for (bdf = list = parse_bdf(direct_pci); bdf; bdf = next_bdf(bdf))
     {
-        bdf = next_bdf(&direct_pci_p);
-        if (!bdf.valid)
-        {
-            break;
-        }
         /* Register real device with the emulated bus */
         pt_dev = register_real_device(e_bus, "DIRECT PCI",
-                                      &bdf, PT_MACHINE_IRQ_AUTO, pci_access);
+                                      bdf, PT_MACHINE_IRQ_AUTO, pci_access);
         if ( pt_dev == NULL )
         {
             PT_LOG("Error: Registration failed (%02x:%02x.%x)\n",
-                   bdf.r_bus, bdf.r_dev, bdf.r_func);
+                   bdf->r_bus, bdf->r_dev, bdf->r_func);
             goto err;
         }
 
@@ -3458,13 +3556,15 @@ int pt_init(PCIBus *e_bus, const char *d
     }
 
     /* Write virtual slots info to xenstore for Control panel use */
-    xenstore_write_vslots(vslots);
+    if (*vslots)
+    {
+        xenstore_write_vslots(vslots);
+    }
 
     status = 0;
 err:
     qemu_free(vslots);
-    free(direct_pci_head);
-
+    free(list);
     return status;
 }
 
Index: ioemu-remote/hw/pci.h
===================================================================
--- ioemu-remote.orig/hw/pci.h  2009-02-17 17:55:37.000000000 +0900
+++ ioemu-remote/hw/pci.h       2009-02-17 17:57:05.000000000 +0900
@@ -115,17 +115,17 @@ PCIBus *pci_bridge_init(PCIBus *bus, int
 #define PCI_DEVFN_IS_PHP(x)   ((x) >= PHP_DEVFN_START && (x) < PHP_DEVFN_END)
 #define PHP_DEVFN_IS_VALID(x) (PCI_DEVFN_IS_PHP(PHP_TO_PCI_DEVFN(x)))
 
-int insert_bdf_to_php_devfn(char *bfd_slt);
+int insert_bdf_to_php_devfn(const char *bfd_slt);
 int test_php_devfn(int devfn);
-int bdf_to_php_devfn(char *bfd_str);
+int bdf_to_php_devfn(const char *bfd_str);
 int power_on_php_devfn(int php_devfn);
 int power_off_php_devfn(int php_devfn);
 
 /* pci_emulation.c */
 #include "hw/pci_emulation.h"
  
-void do_pci_add(char *devname);
-void do_pci_del(char *devname);
+void do_pci_add(const char *devname);
+void do_pci_del(const char *devname);
 
 /* lsi53c895a.c */
 #define LSI_MAX_DEVS 7
Index: ioemu-remote/vl.c
===================================================================
--- ioemu-remote.orig/vl.c      2009-02-17 17:55:37.000000000 +0900
+++ ioemu-remote/vl.c   2009-02-17 17:57:05.000000000 +0900
@@ -3898,7 +3898,7 @@ void qemu_chr_close(CharDriverState *chr
 }
 
 #ifdef CONFIG_PASSTHROUGH
-void do_pci_del(char *devname)
+void do_pci_del(const char *devname)
 {
     int devfn;
     devfn = bdf_to_php_devfn(devname);
@@ -3906,7 +3906,7 @@ void do_pci_del(char *devname)
     acpi_php_del(devfn);
 }
 
-void do_pci_add(char *devname)
+void do_pci_add(const char *devname)
 {
     int devfn;
 

-- 

-- 
Simon Horman
  VA Linux Systems Japan K.K., Sydney, Australia Satellite Office
  H: www.vergenet.net/~horms/             W: www.valinux.co.jp/en


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.