]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bhyve/fwctl.c
MFC r298454 (araujo): Use MIN()/MAX() macros from sys/param.h.
[FreeBSD/stable/10.git] / usr.sbin / bhyve / fwctl.c
1 /*-
2  * Copyright (c) 2015  Peter Grehan <grehan@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 /*
30  * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
31  * but with a request/response messaging protocol.
32  */
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/uio.h>
40
41 #include <assert.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "bhyverun.h"
47 #include "inout.h"
48 #include "fwctl.h"
49
50 /*
51  * Messaging protocol base operations
52  */
53 #define OP_NULL         1
54 #define OP_ECHO         2
55 #define OP_GET          3
56 #define OP_GET_LEN      4
57 #define OP_SET          5
58 #define OP_MAX          OP_SET
59
60 /* I/O ports */
61 #define FWCTL_OUT       0x510
62 #define FWCTL_IN        0x511
63
64 /*
65  * Back-end state-machine
66  */
67 enum state {
68         DORMANT,
69         IDENT_WAIT,
70         IDENT_SEND,
71         REQ,
72         RESP
73 } be_state = DORMANT;
74
75 static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
76 static u_int ident_idx;
77
78 struct op_info {
79         int op;
80         int  (*op_start)(int len);
81         void (*op_data)(uint32_t data, int len);
82         int  (*op_result)(struct iovec **data);
83         void (*op_done)(struct iovec *data);
84 };
85 static struct op_info *ops[OP_MAX+1];
86
87 /* Return 0-padded uint32_t */
88 static uint32_t
89 fwctl_send_rest(uint32_t *data, size_t len)
90 {
91         union {
92                 uint8_t c[4];
93                 uint32_t w;
94         } u;
95         uint8_t *cdata;
96         int i;
97
98         cdata = (uint8_t *) data;
99         u.w = 0;        
100
101         for (i = 0, u.w = 0; i < len; i++)
102                 u.c[i] = *cdata++;
103
104         return (u.w);
105 }
106
107 /*
108  * error op dummy proto - drop all data sent and return an error
109 */
110 static int errop_code;
111
112 static void
113 errop_set(int err)
114 {
115
116         errop_code = err;
117 }
118
119 static int
120 errop_start(int len)
121 {
122         errop_code = ENOENT;
123
124         /* accept any length */
125         return (errop_code);
126 }
127
128 static void
129 errop_data(uint32_t data, int len)
130 {
131
132         /* ignore */
133 }
134
135 static int
136 errop_result(struct iovec **data)
137 {
138
139         /* no data to send back; always successful */
140         *data = NULL;
141         return (errop_code);
142 }
143
144 static void
145 errop_done(struct iovec *data)
146 {
147
148         /* assert data is NULL */
149 }
150
151 static struct op_info errop_info = {
152         .op_start  = errop_start,
153         .op_data   = errop_data,
154         .op_result = errop_result,
155         .op_done   = errop_done
156 };
157
158 /* OID search */
159 SET_DECLARE(ctl_set, struct ctl);
160
161 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
162
163 static struct ctl *
164 ctl_locate(const char *str, int maxlen)
165 {
166         struct ctl *cp, **cpp;
167
168         SET_FOREACH(cpp, ctl_set)  {
169                 cp = *cpp;
170                 if (!strncmp(str, cp->c_oid, maxlen))
171                         return (cp);
172         }
173
174         return (NULL);
175 }
176
177 /* uefi-sysctl get-len */
178 #define FGET_STRSZ      80
179 static struct iovec fget_biov[2];
180 static char fget_str[FGET_STRSZ];
181 static struct {
182         size_t f_sz;
183         uint32_t f_data[1024];
184 } fget_buf;
185 static int fget_cnt;
186 static size_t fget_size;
187
188 static int
189 fget_start(int len)
190 {
191
192         if (len > FGET_STRSZ)
193                 return(E2BIG);
194
195         fget_cnt = 0;
196
197         return (0);
198 }
199
200 static void
201 fget_data(uint32_t data, int len)
202 {
203
204         *((uint32_t *) &fget_str[fget_cnt]) = data;
205         fget_cnt += sizeof(uint32_t);
206 }
207
208 static int
209 fget_result(struct iovec **data, int val)
210 {
211         struct ctl *cp;
212         int err;
213
214         err = 0;
215
216         /* Locate the OID */
217         cp = ctl_locate(fget_str, fget_cnt);
218         if (cp == NULL) {
219                 *data = NULL;
220                 err = ENOENT;
221         } else {
222                 if (val) {
223                         /* For now, copy the len/data into a buffer */
224                         memset(&fget_buf, 0, sizeof(fget_buf));
225                         fget_buf.f_sz = cp->c_len;
226                         memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
227                         fget_biov[0].iov_base = (char *)&fget_buf;
228                         fget_biov[0].iov_len  = sizeof(fget_buf.f_sz) +
229                                 cp->c_len;
230                 } else {
231                         fget_size = cp->c_len;
232                         fget_biov[0].iov_base = (char *)&fget_size;
233                         fget_biov[0].iov_len  = sizeof(fget_size);
234                 }
235
236                 fget_biov[1].iov_base = NULL;
237                 fget_biov[1].iov_len  = 0;
238                 *data = fget_biov;
239         }
240
241         return (err);
242 }
243
244 static void
245 fget_done(struct iovec *data)
246 {
247
248         /* nothing needs to be freed */
249 }
250
251 static int
252 fget_len_result(struct iovec **data)
253 {
254         return (fget_result(data, 0));
255 }
256
257 static int
258 fget_val_result(struct iovec **data)
259 {
260         return (fget_result(data, 1));
261 }
262
263 static struct op_info fgetlen_info = {
264         .op_start  = fget_start,
265         .op_data   = fget_data,
266         .op_result = fget_len_result,
267         .op_done   = fget_done
268 };
269
270 static struct op_info fgetval_info = {
271         .op_start  = fget_start,
272         .op_data   = fget_data,
273         .op_result = fget_val_result,
274         .op_done   = fget_done
275 };
276
277 static struct req_info {
278         int      req_error;
279         u_int    req_count;
280         uint32_t req_size;
281         uint32_t req_type;
282         uint32_t req_txid;
283         struct op_info *req_op;
284         int      resp_error;
285         int      resp_count;
286         int      resp_size;
287         int      resp_off;
288         struct iovec *resp_biov;
289 } rinfo;
290
291 static void
292 fwctl_response_done(void)
293 {
294
295         (*rinfo.req_op->op_done)(rinfo.resp_biov);
296
297         /* reinit the req data struct */
298         memset(&rinfo, 0, sizeof(rinfo));
299 }
300
301 static void
302 fwctl_request_done(void)
303 {
304
305         rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
306
307         /* XXX only a single vector supported at the moment */
308         rinfo.resp_off = 0;
309         if (rinfo.resp_biov == NULL) {
310                 rinfo.resp_size = 0;
311         } else {
312                 rinfo.resp_size = rinfo.resp_biov[0].iov_len;
313         }
314 }
315
316 static int
317 fwctl_request_start(void)
318 {
319         int err;
320
321         /* Data size doesn't include header */
322         rinfo.req_size -= 12;
323
324         rinfo.req_op = &errop_info;
325         if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
326                 rinfo.req_op = ops[rinfo.req_type];
327
328         err = (*rinfo.req_op->op_start)(rinfo.req_size);
329
330         if (err) {
331                 errop_set(err);
332                 rinfo.req_op = &errop_info;
333         }
334
335         /* Catch case of zero-length message here */
336         if (rinfo.req_size == 0) {
337                 fwctl_request_done();
338                 return (1);
339         }
340
341         return (0);
342 }
343
344 static int
345 fwctl_request_data(uint32_t value)
346 {
347         int remlen;
348
349         /* Make sure remaining size is >= 0 */
350         rinfo.req_size -= sizeof(uint32_t);
351         remlen = MAX(rinfo.req_size, 0);
352
353         (*rinfo.req_op->op_data)(value, remlen);
354
355         if (rinfo.req_size < sizeof(uint32_t)) {
356                 fwctl_request_done();
357                 return (1);
358         }
359
360         return (0);
361 }
362
363 static int
364 fwctl_request(uint32_t value)
365 {
366
367         int ret;
368
369         ret = 0;
370
371         switch (rinfo.req_count) {
372         case 0:
373                 /* Verify size */
374                 if (value < 12) {
375                         printf("msg size error");
376                         exit(1);
377                 }
378                 rinfo.req_size = value;
379                 rinfo.req_count = 1;
380                 break;
381         case 1:
382                 rinfo.req_type = value;
383                 rinfo.req_count++;
384                 break;
385         case 2:
386                 rinfo.req_txid = value;
387                 rinfo.req_count++;
388                 ret = fwctl_request_start();
389                 break;
390         default:
391                 ret = fwctl_request_data(value);
392                 break;
393         }
394
395         return (ret);
396 }
397
398 static int
399 fwctl_response(uint32_t *retval)
400 {
401         uint32_t *dp;
402         int remlen;
403
404         switch(rinfo.resp_count) {
405         case 0:
406                 /* 4 x u32 header len + data */
407                 *retval = 4*sizeof(uint32_t) +
408                     roundup(rinfo.resp_size, sizeof(uint32_t));
409                 rinfo.resp_count++;
410                 break;
411         case 1:
412                 *retval = rinfo.req_type;
413                 rinfo.resp_count++;
414                 break;
415         case 2:
416                 *retval = rinfo.req_txid;
417                 rinfo.resp_count++;
418                 break;
419         case 3:
420                 *retval = rinfo.resp_error;
421                 rinfo.resp_count++;
422                 break;
423         default:
424                 remlen = rinfo.resp_size - rinfo.resp_off;
425                 dp = (uint32_t *)
426                     ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
427                 if (remlen >= sizeof(uint32_t)) {
428                         *retval = *dp;
429                 } else if (remlen > 0) {
430                         *retval = fwctl_send_rest(dp, remlen);
431                 }
432                 rinfo.resp_off += sizeof(uint32_t);
433                 break;
434         }
435
436         if (rinfo.resp_count > 3 &&
437             rinfo.resp_size - rinfo.resp_off <= 0) {
438                 fwctl_response_done();
439                 return (1);
440         }
441
442         return (0);
443 }
444
445
446 /*
447  * i/o port handling.
448  */
449 static uint8_t
450 fwctl_inb(void)
451 {
452         uint8_t retval;
453
454         retval = 0xff;
455
456         switch (be_state) {
457         case IDENT_SEND:
458                 retval = sig[ident_idx++];
459                 if (ident_idx >= sizeof(sig))
460                         be_state = REQ;
461                 break;
462         default:
463                 break;
464         }
465
466         return (retval);
467 }
468
469 static void
470 fwctl_outw(uint16_t val)
471 {
472         switch (be_state) {
473         case IDENT_WAIT:
474                 if (val == 0) {
475                         be_state = IDENT_SEND;
476                         ident_idx = 0;
477                 }
478                 break;
479         default:
480                 /* ignore */
481                 break;
482         }
483 }
484
485 static uint32_t
486 fwctl_inl(void)
487 {
488         uint32_t retval;
489
490         switch (be_state) {
491         case RESP:
492                 if (fwctl_response(&retval))
493                         be_state = REQ;
494                 break;
495         default:
496                 retval = 0xffffffff;
497                 break;
498         }
499
500         return (retval);
501 }
502
503 static void
504 fwctl_outl(uint32_t val)
505 {
506
507         switch (be_state) {
508         case REQ:
509                 if (fwctl_request(val))
510                         be_state = RESP;
511         default:
512                 break;
513         }
514
515 }
516
517 static int
518 fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
519     uint32_t *eax, void *arg)
520 {
521
522         if (in) {
523                 if (bytes == 1)
524                         *eax = fwctl_inb();
525                 else if (bytes == 4)
526                         *eax = fwctl_inl();
527                 else
528                         *eax = 0xffff;
529         } else {
530                 if (bytes == 2)
531                         fwctl_outw(*eax);
532                 else if (bytes == 4)
533                         fwctl_outl(*eax);
534         }
535
536         return (0);
537 }
538 INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
539 INOUT_PORT(fwctl_rreg, FWCTL_IN,  IOPORT_F_IN,    fwctl_handler);
540
541 void
542 fwctl_init(void)
543 {
544
545         ops[OP_GET_LEN] = &fgetlen_info;
546         ops[OP_GET]     = &fgetval_info;
547
548         be_state = IDENT_WAIT;
549 }