]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/fwctl.c
bhyve: Address warnings about potential unaligned accesses in fwctl.c
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / fwctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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         DORMANT,
71         IDENT_WAIT,
72         IDENT_SEND,
73         REQ,
74         RESP
75 } be_state = DORMANT;
76
77 static uint8_t sig[] = { 'B', 'H', 'Y', 'V' };
78 static u_int ident_idx;
79
80 struct op_info {
81         int op;
82         int  (*op_start)(uint32_t len);
83         void (*op_data)(uint32_t data, uint32_t len);
84         int  (*op_result)(struct iovec **data);
85         void (*op_done)(struct iovec *data);
86 };
87 static struct op_info *ops[OP_MAX+1];
88
89 /* Return 0-padded uint32_t */
90 static uint32_t
91 fwctl_send_rest(uint8_t *data, size_t len)
92 {
93         union {
94                 uint8_t c[4];
95                 uint32_t w;
96         } u;
97         size_t i;
98
99         u.w = 0;
100         for (i = 0; i < len; i++)
101                 u.c[i] = *data++;
102
103         return (u.w);
104 }
105
106 /*
107  * error op dummy proto - drop all data sent and return an error
108 */
109 static int errop_code;
110
111 static void
112 errop_set(int err)
113 {
114
115         errop_code = err;
116 }
117
118 static int
119 errop_start(uint32_t len __unused)
120 {
121         errop_code = ENOENT;
122
123         /* accept any length */
124         return (errop_code);
125 }
126
127 static void
128 errop_data(uint32_t data __unused, uint32_t len __unused)
129 {
130
131         /* ignore */
132 }
133
134 static int
135 errop_result(struct iovec **data)
136 {
137
138         /* no data to send back; always successful */
139         *data = NULL;
140         return (errop_code);
141 }
142
143 static void
144 errop_done(struct iovec *data __unused)
145 {
146
147         /* assert data is NULL */
148 }
149
150 static struct op_info errop_info = {
151         .op_start  = errop_start,
152         .op_data   = errop_data,
153         .op_result = errop_result,
154         .op_done   = errop_done
155 };
156
157 /* OID search */
158 SET_DECLARE(ctl_set, struct ctl);
159
160 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
161
162 static struct ctl *
163 ctl_locate(const char *str, int maxlen)
164 {
165         struct ctl *cp, **cpp;
166
167         SET_FOREACH(cpp, ctl_set)  {
168                 cp = *cpp;
169                 if (!strncmp(str, cp->c_oid, maxlen))
170                         return (cp);
171         }
172
173         return (NULL);
174 }
175
176 /* uefi-sysctl get-len */
177 #define FGET_STRSZ      80
178 static struct iovec fget_biov[2];
179 static char fget_str[FGET_STRSZ];
180 static struct {
181         size_t f_sz;
182         uint32_t f_data[1024];
183 } fget_buf;
184 static int fget_cnt;
185 static size_t fget_size;
186
187 static int
188 fget_start(uint32_t len)
189 {
190
191         if (len > FGET_STRSZ)
192                 return(E2BIG);
193
194         fget_cnt = 0;
195
196         return (0);
197 }
198
199 static void
200 fget_data(uint32_t data, uint32_t len __unused)
201 {
202
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         if (rinfo.req_size <= sizeof(uint32_t))
349                 rinfo.req_size = 0;
350         else
351                 rinfo.req_size -= sizeof(uint32_t);
352
353         (*rinfo.req_op->op_data)(value, rinfo.req_size);
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(4);
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         uint8_t *dp;
402         ssize_t 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 = (uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off;
426                 if (remlen >= (ssize_t)sizeof(uint32_t)) {
427                         memcpy(retval, dp, sizeof(uint32_t));
428                 } else if (remlen > 0) {
429                         *retval = fwctl_send_rest(dp, remlen);
430                 }
431                 rinfo.resp_off += sizeof(uint32_t);
432                 break;
433         }
434
435         if (rinfo.resp_count > 3 &&
436             rinfo.resp_off >= rinfo.resp_size) {
437                 fwctl_response_done();
438                 return (1);
439         }
440
441         return (0);
442 }
443
444
445 /*
446  * i/o port handling.
447  */
448 static uint8_t
449 fwctl_inb(void)
450 {
451         uint8_t retval;
452
453         retval = 0xff;
454
455         switch (be_state) {
456         case IDENT_SEND:
457                 retval = sig[ident_idx++];
458                 if (ident_idx >= sizeof(sig))
459                         be_state = REQ;
460                 break;
461         default:
462                 break;
463         }
464
465         return (retval);
466 }
467
468 static void
469 fwctl_outw(uint16_t val)
470 {
471         if (be_state == DORMANT) {
472                 return;
473         }
474
475         if (val == 0) {
476                 /*
477                  * The guest wants to read the signature. It's possible that the
478                  * guest is unaware of the fwctl state at this moment. For that
479                  * reason, reset the state machine unconditionally.
480                  */
481                 be_state = IDENT_SEND;
482                 ident_idx = 0;
483         }
484 }
485
486 static uint32_t
487 fwctl_inl(void)
488 {
489         uint32_t retval;
490
491         switch (be_state) {
492         case RESP:
493                 if (fwctl_response(&retval))
494                         be_state = REQ;
495                 break;
496         default:
497                 retval = 0xffffffff;
498                 break;
499         }
500
501         return (retval);
502 }
503
504 static void
505 fwctl_outl(uint32_t val)
506 {
507
508         switch (be_state) {
509         case REQ:
510                 if (fwctl_request(val))
511                         be_state = RESP;
512         default:
513                 break;
514         }
515
516 }
517
518 static int
519 fwctl_handler(struct vmctx *ctx __unused, int vcpu __unused, int in,
520     int port __unused, int bytes, uint32_t *eax, void *arg __unused)
521 {
522
523         if (in) {
524                 if (bytes == 1)
525                         *eax = fwctl_inb();
526                 else if (bytes == 4)
527                         *eax = fwctl_inl();
528                 else
529                         *eax = 0xffff;
530         } else {
531                 if (bytes == 2)
532                         fwctl_outw(*eax);
533                 else if (bytes == 4)
534                         fwctl_outl(*eax);
535         }
536
537         return (0);
538 }
539
540 void
541 fwctl_init(void)
542 {
543         struct inout_port iop;
544         int error;
545
546         bzero(&iop, sizeof(iop));
547         iop.name = "fwctl_wreg";
548         iop.port = FWCTL_OUT;
549         iop.size = 1;
550         iop.flags = IOPORT_F_INOUT;
551         iop.handler = fwctl_handler;
552         
553         error = register_inout(&iop);
554         assert(error == 0);
555
556         bzero(&iop, sizeof(iop));
557         iop.name = "fwctl_rreg";
558         iop.port = FWCTL_IN;
559         iop.size = 1;
560         iop.flags = IOPORT_F_IN;
561         iop.handler = fwctl_handler;
562
563         error = register_inout(&iop);
564         assert(error == 0);
565
566         ops[OP_GET_LEN] = &fgetlen_info;
567         ops[OP_GET]     = &fgetval_info;
568
569         be_state = IDENT_WAIT;
570 }