From mboxrd@z Thu Jan 1 00:00:00 1970 From: Simon Horman Subject: [rfc 16/18] ioemu: non-destructive parsing of PCI assignement strings Date: Tue, 17 Feb 2009 20:08:04 +1100 Message-ID: <20090217091547.850078642@vergenet.net> References: <20090217090748.580007796@vergenet.net> Return-path: Content-Disposition: inline; filename=regex-parser-3f23188224b7ce69fcf13f52cb1c7977a5372900.patch List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: xen-devel@lists.xensource.com Cc: Ian Jackson List-Id: xen-devel@lists.xenproject.org Signed-off-by: Simon Horman --- 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 +#include + +#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