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