1 #define _GNU_SOURCE
2 
3 #include <ctype.h>
4 
5 #include "libxlu_internal.h"
6 
7 
8 #define XLU__PCI_ERR(_c, _x, _a...) \
9     if((_c) && (_c)->report) fprintf((_c)->report, _x, ##_a)
10 
parse_bdf(libxl_device_pci * pci,const char * str,const char ** endp)11 static int parse_bdf(libxl_device_pci *pci, const char *str, const char **endp)
12 {
13     const char *ptr = str;
14     unsigned int colons = 0;
15     unsigned int domain, bus, dev, func;
16     int n;
17 
18     /* Count occurrences of ':' to detrmine presence/absence of the 'domain' */
19     while (isxdigit(*ptr) || *ptr == ':') {
20         if (*ptr == ':')
21             colons++;
22         ptr++;
23     }
24 
25     ptr = str;
26     switch (colons) {
27     case 1:
28         domain = 0;
29         if (sscanf(ptr, "%x:%x.%n", &bus, &dev, &n) != 2)
30             return ERROR_INVAL;
31         break;
32     case 2:
33         if (sscanf(ptr, "%x:%x:%x.%n", &domain, &bus, &dev, &n) != 3)
34             return ERROR_INVAL;
35         break;
36     default:
37         return ERROR_INVAL;
38     }
39 
40     if (domain > 0xffff || bus > 0xff || dev > 0x1f)
41         return ERROR_INVAL;
42 
43     ptr += n;
44     if (*ptr == '*') {
45         pci->vfunc_mask = LIBXL_PCI_FUNC_ALL;
46         func = 0;
47         ptr++;
48     } else {
49         if (sscanf(ptr, "%x%n", &func, &n) != 1)
50             return ERROR_INVAL;
51         if (func > 7)
52             return ERROR_INVAL;
53         pci->vfunc_mask = 1;
54         ptr += n;
55     }
56 
57     pci->domain = domain;
58     pci->bus = bus;
59     pci->dev = dev;
60     pci->func = func;
61 
62     if (endp)
63         *endp = ptr;
64 
65     return 0;
66 }
67 
parse_vslot(uint32_t * vdevfnp,const char * str,const char ** endp)68 static int parse_vslot(uint32_t *vdevfnp, const char *str, const char **endp)
69 {
70     const char *ptr = str;
71     unsigned int val;
72     int n;
73 
74     if (sscanf(ptr, "%x%n", &val, &n) != 1)
75         return ERROR_INVAL;
76 
77     if (val > 0x1f)
78         return ERROR_INVAL;
79 
80     ptr += n;
81 
82     *vdevfnp = val << 3;
83 
84     if (endp)
85         *endp = ptr;
86 
87     return 0;
88 }
89 
parse_key_val(char ** keyp,char ** valp,const char * str,const char ** endp)90 static int parse_key_val(char **keyp, char**valp, const char *str,
91                          const char **endp)
92 {
93     const char *ptr = str;
94     char *key, *val;
95 
96     while (*ptr != '=' && *ptr != '\0')
97         ptr++;
98 
99     if (*ptr == '\0')
100         return ERROR_INVAL;
101 
102     key = strndup(str, ptr - str);
103     if (!key)
104         return ERROR_NOMEM;
105 
106     str = ++ptr; /* skip '=' */
107     while (*ptr != ',' && *ptr != '\0')
108         ptr++;
109 
110     val = strndup(str, ptr - str);
111     if (!val) {
112         free(key);
113         return ERROR_NOMEM;
114     }
115 
116     if (*ptr == ',')
117         ptr++;
118 
119     *keyp = key;
120     *valp = val;
121     *endp = ptr;
122 
123     return 0;
124 }
125 
parse_rdm_policy(XLU_Config * cfg,libxl_rdm_reserve_policy * policy,const char * str)126 static int parse_rdm_policy(XLU_Config *cfg, libxl_rdm_reserve_policy *policy,
127                             const char *str)
128 {
129     int ret = libxl_rdm_reserve_policy_from_string(str, policy);
130 
131     if (ret)
132         XLU__PCI_ERR(cfg, "Unknown RDM policy: %s", str);
133 
134     return ret;
135 }
136 
xlu_pci_parse_bdf(XLU_Config * cfg,libxl_device_pci * pci,const char * str)137 int xlu_pci_parse_bdf(XLU_Config *cfg, libxl_device_pci *pci, const char *str)
138 {
139     return parse_bdf(pci, str, NULL);
140 }
141 
xlu_pci_parse_spec_string(XLU_Config * cfg,libxl_device_pci * pci,const char * str)142 int xlu_pci_parse_spec_string(XLU_Config *cfg, libxl_device_pci *pci,
143                               const char *str)
144 {
145     const char *ptr = str;
146     bool bdf_present = false;
147     bool name_present = false;
148     int ret;
149 
150     /* Attempt to parse 'bdf' as positional parameter */
151     ret = parse_bdf(pci, ptr, &ptr);
152     if (!ret) {
153         bdf_present = true;
154 
155         /* Check whether 'vslot' is present */
156         if (*ptr == '@') {
157             ++ptr;
158             ret = parse_vslot(&pci->vdevfn, ptr, &ptr);
159             if (ret)
160                 return ret;
161         }
162         if (*ptr == ',')
163             ptr++;
164         else if (*ptr != '\0')
165             return ERROR_INVAL;
166     }
167 
168     /* Parse the rest as 'key=val' pairs */
169     while (*ptr != '\0') {
170         char *key, *val;
171 
172         ret = parse_key_val(&key, &val, ptr, &ptr);
173         if (ret)
174             return ret;
175 
176         if (!strcmp(key, "bdf")) {
177             ret = parse_bdf(pci, val, NULL);
178             if (!ret) bdf_present = true;
179         } else if (!strcmp(key, "vslot")) {
180             ret = parse_vslot(&pci->vdevfn, val, NULL);
181         } else if (!strcmp(key, "permissive")) {
182             pci->permissive = atoi(val);
183         } else if (!strcmp(key, "msitranslate")) {
184             pci->msitranslate = atoi(val);
185         } else if (!strcmp(key, "seize")) {
186             pci->seize= atoi(val);
187         } else if (!strcmp(key, "power_mgmt")) {
188             pci->power_mgmt = atoi(val);
189         } else if (!strcmp(key, "rdm_policy")) {
190             ret = parse_rdm_policy(cfg, &pci->rdm_policy, val);
191         } else if (!strcmp(key, "name")) {
192             name_present = true;
193             pci->name = strdup(val);
194             if (!pci->name) ret = ERROR_NOMEM;
195         } else {
196             XLU__PCI_ERR(cfg, "Unknown PCI_SPEC_STRING option: %s", key);
197             ret = ERROR_INVAL;
198         }
199 
200         free(key);
201         free(val);
202 
203         if (ret)
204             return ret;
205     }
206 
207     if (!(bdf_present ^ name_present))
208         return ERROR_INVAL;
209 
210     return 0;
211 }
212 
xlu_rdm_parse(XLU_Config * cfg,libxl_rdm_reserve * rdm,const char * str)213 int xlu_rdm_parse(XLU_Config *cfg, libxl_rdm_reserve *rdm, const char *str)
214 {
215 #define STATE_TYPE           0
216 #define STATE_RDM_STRATEGY   1
217 #define STATE_RESERVE_POLICY 2
218 #define STATE_TERMINAL       3
219 
220     unsigned state = STATE_TYPE;
221     char *buf2, *tok, *ptr, *end;
222 
223     if (NULL == (buf2 = ptr = strdup(str)))
224         return ERROR_NOMEM;
225 
226     for (tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) {
227         switch(state) {
228         case STATE_TYPE:
229             if (*ptr == '=') {
230                 *ptr = '\0';
231                 if (!strcmp(tok, "strategy")) {
232                     state = STATE_RDM_STRATEGY;
233                 } else if (!strcmp(tok, "policy")) {
234                     state = STATE_RESERVE_POLICY;
235                 } else {
236                     XLU__PCI_ERR(cfg, "Unknown RDM state option: %s", tok);
237                     goto parse_error;
238                 }
239                 tok = ptr + 1;
240             }
241             break;
242         case STATE_RDM_STRATEGY:
243             if (*ptr == '\0' || *ptr == ',') {
244                 state = *ptr == ',' ? STATE_TYPE : STATE_TERMINAL;
245                 *ptr = '\0';
246                 if (!strcmp(tok, "host")) {
247                     rdm->strategy = LIBXL_RDM_RESERVE_STRATEGY_HOST;
248                 } else {
249                     XLU__PCI_ERR(cfg, "Unknown RDM strategy option: %s", tok);
250                     goto parse_error;
251                 }
252                 tok = ptr + 1;
253             }
254             break;
255         case STATE_RESERVE_POLICY:
256             if (*ptr == ',' || *ptr == '\0') {
257                 state = *ptr == ',' ? STATE_TYPE : STATE_TERMINAL;
258                 *ptr = '\0';
259                 if (!parse_rdm_policy(cfg, &rdm->policy, tok))
260                     goto parse_error;
261                 tok = ptr + 1;
262             }
263         default:
264             break;
265         }
266     }
267 
268     if (tok != ptr || state != STATE_TERMINAL)
269         goto parse_error;
270 
271     free(buf2);
272 
273     return 0;
274 
275 parse_error:
276     free(buf2);
277     return ERROR_INVAL;
278 
279 #undef STATE_TYPE
280 #undef STATE_RDM_STRATEGY
281 #undef STATE_RESERVE_POLICY
282 #undef STATE_TERMINAL
283 }
284 
285 /*
286  * Local variables:
287  * mode: C
288  * c-basic-offset: 4
289  * indent-tabs-mode: nil
290  * End:
291  */
292