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