#define _GNU_SOURCE #include #include "libxlu_internal.h" #define XLU__PCI_ERR(_c, _x, _a...) \ if((_c) && (_c)->report) fprintf((_c)->report, _x, ##_a) static int parse_bdf(libxl_device_pci *pci, const char *str, const char **endp) { const char *ptr = str; unsigned int colons = 0; unsigned int domain, bus, dev, func; int n; /* Count occurrences of ':' to detrmine presence/absence of the 'domain' */ while (isxdigit(*ptr) || *ptr == ':') { if (*ptr == ':') colons++; ptr++; } ptr = str; switch (colons) { case 1: domain = 0; if (sscanf(ptr, "%x:%x.%n", &bus, &dev, &n) != 2) return ERROR_INVAL; break; case 2: if (sscanf(ptr, "%x:%x:%x.%n", &domain, &bus, &dev, &n) != 3) return ERROR_INVAL; break; default: return ERROR_INVAL; } if (domain > 0xffff || bus > 0xff || dev > 0x1f) return ERROR_INVAL; ptr += n; if (*ptr == '*') { pci->vfunc_mask = LIBXL_PCI_FUNC_ALL; func = 0; ptr++; } else { if (sscanf(ptr, "%x%n", &func, &n) != 1) return ERROR_INVAL; if (func > 7) return ERROR_INVAL; pci->vfunc_mask = 1; ptr += n; } pci->domain = domain; pci->bus = bus; pci->dev = dev; pci->func = func; if (endp) *endp = ptr; return 0; } static int parse_vslot(uint32_t *vdevfnp, const char *str, const char **endp) { const char *ptr = str; unsigned int val; int n; if (sscanf(ptr, "%x%n", &val, &n) != 1) return ERROR_INVAL; if (val > 0x1f) return ERROR_INVAL; ptr += n; *vdevfnp = val << 3; if (endp) *endp = ptr; return 0; } static int parse_key_val(char **keyp, char**valp, const char *str, const char **endp) { const char *ptr = str; char *key, *val; while (*ptr != '=' && *ptr != '\0') ptr++; if (*ptr == '\0') return ERROR_INVAL; key = strndup(str, ptr - str); if (!key) return ERROR_NOMEM; str = ++ptr; /* skip '=' */ while (*ptr != ',' && *ptr != '\0') ptr++; val = strndup(str, ptr - str); if (!val) { free(key); return ERROR_NOMEM; } if (*ptr == ',') ptr++; *keyp = key; *valp = val; *endp = ptr; return 0; } static int parse_rdm_policy(XLU_Config *cfg, libxl_rdm_reserve_policy *policy, const char *str) { int ret = libxl_rdm_reserve_policy_from_string(str, policy); if (ret) XLU__PCI_ERR(cfg, "Unknown RDM policy: %s", str); return ret; } int xlu_pci_parse_bdf(XLU_Config *cfg, libxl_device_pci *pci, const char *str) { return parse_bdf(pci, str, NULL); } int xlu_pci_parse_spec_string(XLU_Config *cfg, libxl_device_pci *pci, const char *str) { const char *ptr = str; bool bdf_present = false; bool name_present = false; int ret; /* Attempt to parse 'bdf' as positional parameter */ ret = parse_bdf(pci, ptr, &ptr); if (!ret) { bdf_present = true; /* Check whether 'vslot' is present */ if (*ptr == '@') { ++ptr; ret = parse_vslot(&pci->vdevfn, ptr, &ptr); if (ret) return ret; } if (*ptr == ',') ptr++; else if (*ptr != '\0') return ERROR_INVAL; } /* Parse the rest as 'key=val' pairs */ while (*ptr != '\0') { char *key, *val; ret = parse_key_val(&key, &val, ptr, &ptr); if (ret) return ret; if (!strcmp(key, "bdf")) { ret = parse_bdf(pci, val, NULL); if (!ret) bdf_present = true; } else if (!strcmp(key, "vslot")) { ret = parse_vslot(&pci->vdevfn, val, NULL); } else if (!strcmp(key, "permissive")) { pci->permissive = atoi(val); } else if (!strcmp(key, "msitranslate")) { pci->msitranslate = atoi(val); } else if (!strcmp(key, "seize")) { pci->seize= atoi(val); } else if (!strcmp(key, "power_mgmt")) { pci->power_mgmt = atoi(val); } else if (!strcmp(key, "rdm_policy")) { ret = parse_rdm_policy(cfg, &pci->rdm_policy, val); } else if (!strcmp(key, "name")) { name_present = true; pci->name = strdup(val); if (!pci->name) ret = ERROR_NOMEM; } else { XLU__PCI_ERR(cfg, "Unknown PCI_SPEC_STRING option: %s", key); ret = ERROR_INVAL; } free(key); free(val); if (ret) return ret; } if (!(bdf_present ^ name_present)) return ERROR_INVAL; return 0; } int xlu_rdm_parse(XLU_Config *cfg, libxl_rdm_reserve *rdm, const char *str) { #define STATE_TYPE 0 #define STATE_RDM_STRATEGY 1 #define STATE_RESERVE_POLICY 2 #define STATE_TERMINAL 3 unsigned state = STATE_TYPE; char *buf2, *tok, *ptr, *end; if (NULL == (buf2 = ptr = strdup(str))) return ERROR_NOMEM; for (tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) { switch(state) { case STATE_TYPE: if (*ptr == '=') { *ptr = '\0'; if (!strcmp(tok, "strategy")) { state = STATE_RDM_STRATEGY; } else if (!strcmp(tok, "policy")) { state = STATE_RESERVE_POLICY; } else { XLU__PCI_ERR(cfg, "Unknown RDM state option: %s", tok); goto parse_error; } tok = ptr + 1; } break; case STATE_RDM_STRATEGY: if (*ptr == '\0' || *ptr == ',') { state = *ptr == ',' ? STATE_TYPE : STATE_TERMINAL; *ptr = '\0'; if (!strcmp(tok, "host")) { rdm->strategy = LIBXL_RDM_RESERVE_STRATEGY_HOST; } else { XLU__PCI_ERR(cfg, "Unknown RDM strategy option: %s", tok); goto parse_error; } tok = ptr + 1; } break; case STATE_RESERVE_POLICY: if (*ptr == ',' || *ptr == '\0') { state = *ptr == ',' ? STATE_TYPE : STATE_TERMINAL; *ptr = '\0'; if (!parse_rdm_policy(cfg, &rdm->policy, tok)) goto parse_error; tok = ptr + 1; } default: break; } } if (tok != ptr || state != STATE_TERMINAL) goto parse_error; free(buf2); return 0; parse_error: free(buf2); return ERROR_INVAL; #undef STATE_TYPE #undef STATE_RDM_STRATEGY #undef STATE_RESERVE_POLICY #undef STATE_TERMINAL } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */