]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/fwctl.c
bhnd(9): Fix a few mandoc related issues
[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 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(uint32_t *data, size_t len)
92 {
93         union {
94                 uint8_t c[4];
95                 uint32_t w;
96         } u;
97         uint8_t *cdata;
98         int i;
99
100         cdata = (uint8_t *) data;
101         u.w = 0;        
102
103         for (i = 0, u.w = 0; i < len; i++)
104                 u.c[i] = *cdata++;
105
106         return (u.w);
107 }
108
109 /*
110  * error op dummy proto - drop all data sent and return an error
111 */
112 static int errop_code;
113
114 static void
115 errop_set(int err)
116 {
117
118         errop_code = err;
119 }
120
121 static int
122 errop_start(uint32_t len)
123 {
124         errop_code = ENOENT;
125
126         /* accept any length */
127         return (errop_code);
128 }
129
130 static void
131 errop_data(uint32_t data, uint32_t len)
132 {
133
134         /* ignore */
135 }
136
137 static int
138 errop_result(struct iovec **data)
139 {
140
141         /* no data to send back; always successful */
142         *data = NULL;
143         return (errop_code);
144 }
145
146 static void
147 errop_done(struct iovec *data)
148 {
149
150         /* assert data is NULL */
151 }
152
153 static struct op_info errop_info = {
154         .op_start  = errop_start,
155         .op_data   = errop_data,
156         .op_result = errop_result,
157         .op_done   = errop_done
158 };
159
160 /* OID search */
161 SET_DECLARE(ctl_set, struct ctl);
162
163 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
164
165 static struct ctl *
166 ctl_locate(const char *str, int maxlen)
167 {
168         struct ctl *cp, **cpp;
169
170         SET_FOREACH(cpp, ctl_set)  {
171                 cp = *cpp;
172                 if (!strncmp(str, cp->c_oid, maxlen))
173                         return (cp);
174         }
175
176         return (NULL);
177 }
178
179 /* uefi-sysctl get-len */
180 #define FGET_STRSZ      80
181 static struct iovec fget_biov[2];
182 static char fget_str[FGET_STRSZ];
183 static struct {
184         size_t f_sz;
185         uint32_t f_data[1024];
186 } fget_buf;
187 static int fget_cnt;
188 static size_t fget_size;
189
190 static int
191 fget_start(uint32_t len)
192 {
193
194         if (len > FGET_STRSZ)
195                 return(E2BIG);
196
197         fget_cnt = 0;
198
199         return (0);
200 }
201
202 static void
203 fget_data(uint32_t data, uint32_t len)
204 {
205
206         *((uint32_t *) &fget_str[fget_cnt]) = data;
207         fget_cnt += sizeof(uint32_t);
208 }
209
210 static int
211 fget_result(struct iovec **data, int val)
212 {
213         struct ctl *cp;
214         int err;
215
216         err = 0;
217
218         /* Locate the OID */
219         cp = ctl_locate(fget_str, fget_cnt);
220         if (cp == NULL) {
221                 *data = NULL;
222                 err = ENOENT;
223         } else {
224                 if (val) {
225                         /* For now, copy the len/data into a buffer */
226                         memset(&fget_buf, 0, sizeof(fget_buf));
227                         fget_buf.f_sz = cp->c_len;
228                         memcpy(fget_buf.f_data, cp->c_data, cp->c_len);
229                         fget_biov[0].iov_base = (char *)&fget_buf;
230                         fget_biov[0].iov_len  = sizeof(fget_buf.f_sz) +
231                                 cp->c_len;
232                 } else {
233                         fget_size = cp->c_len;
234                         fget_biov[0].iov_base = (char *)&fget_size;
235                         fget_biov[0].iov_len  = sizeof(fget_size);
236                 }
237
238                 fget_biov[1].iov_base = NULL;
239                 fget_biov[1].iov_len  = 0;
240                 *data = fget_biov;
241         }
242
243         return (err);
244 }
245
246 static void
247 fget_done(struct iovec *data)
248 {
249
250         /* nothing needs to be freed */
251 }
252
253 static int
254 fget_len_result(struct iovec **data)
255 {
256         return (fget_result(data, 0));
257 }
258
259 static int
260 fget_val_result(struct iovec **data)
261 {
262         return (fget_result(data, 1));
263 }
264
265 static struct op_info fgetlen_info = {
266         .op_start  = fget_start,
267         .op_data   = fget_data,
268         .op_result = fget_len_result,
269         .op_done   = fget_done
270 };
271
272 static struct op_info fgetval_info = {
273         .op_start  = fget_start,
274         .op_data   = fget_data,
275         .op_result = fget_val_result,
276         .op_done   = fget_done
277 };
278
279 static struct req_info {
280         int      req_error;
281         u_int    req_count;
282         uint32_t req_size;
283         uint32_t req_type;
284         uint32_t req_txid;
285         struct op_info *req_op;
286         int      resp_error;
287         int      resp_count;
288         size_t   resp_size;
289         size_t   resp_off;
290         struct iovec *resp_biov;
291 } rinfo;
292
293 static void
294 fwctl_response_done(void)
295 {
296
297         (*rinfo.req_op->op_done)(rinfo.resp_biov);
298
299         /* reinit the req data struct */
300         memset(&rinfo, 0, sizeof(rinfo));
301 }
302
303 static void
304 fwctl_request_done(void)
305 {
306
307         rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
308
309         /* XXX only a single vector supported at the moment */
310         rinfo.resp_off = 0;
311         if (rinfo.resp_biov == NULL) {
312                 rinfo.resp_size = 0;
313         } else {
314                 rinfo.resp_size = rinfo.resp_biov[0].iov_len;
315         }
316 }
317
318 static int
319 fwctl_request_start(void)
320 {
321         int err;
322
323         /* Data size doesn't include header */
324         rinfo.req_size -= 12;
325
326         rinfo.req_op = &errop_info;
327         if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL)
328                 rinfo.req_op = ops[rinfo.req_type];
329
330         err = (*rinfo.req_op->op_start)(rinfo.req_size);
331
332         if (err) {
333                 errop_set(err);
334                 rinfo.req_op = &errop_info;
335         }
336
337         /* Catch case of zero-length message here */
338         if (rinfo.req_size == 0) {
339                 fwctl_request_done();
340                 return (1);
341         }
342
343         return (0);
344 }
345
346 static int
347 fwctl_request_data(uint32_t value)
348 {
349
350         /* Make sure remaining size is >= 0 */
351         if (rinfo.req_size <= sizeof(uint32_t))
352                 rinfo.req_size = 0;
353         else
354                 rinfo.req_size -= sizeof(uint32_t);
355
356         (*rinfo.req_op->op_data)(value, rinfo.req_size);
357
358         if (rinfo.req_size < sizeof(uint32_t)) {
359                 fwctl_request_done();
360                 return (1);
361         }
362
363         return (0);
364 }
365
366 static int
367 fwctl_request(uint32_t value)
368 {
369
370         int ret;
371
372         ret = 0;
373
374         switch (rinfo.req_count) {
375         case 0:
376                 /* Verify size */
377                 if (value < 12) {
378                         printf("msg size error");
379                         exit(4);
380                 }
381                 rinfo.req_size = value;
382                 rinfo.req_count = 1;
383                 break;
384         case 1:
385                 rinfo.req_type = value;
386                 rinfo.req_count++;
387                 break;
388         case 2:
389                 rinfo.req_txid = value;
390                 rinfo.req_count++;
391                 ret = fwctl_request_start();
392                 break;
393         default:
394                 ret = fwctl_request_data(value);
395                 break;
396         }
397
398         return (ret);
399 }
400
401 static int
402 fwctl_response(uint32_t *retval)
403 {
404         uint32_t *dp;
405         ssize_t remlen;
406
407         switch(rinfo.resp_count) {
408         case 0:
409                 /* 4 x u32 header len + data */
410                 *retval = 4*sizeof(uint32_t) +
411                     roundup(rinfo.resp_size, sizeof(uint32_t));
412                 rinfo.resp_count++;
413                 break;
414         case 1:
415                 *retval = rinfo.req_type;
416                 rinfo.resp_count++;
417                 break;
418         case 2:
419                 *retval = rinfo.req_txid;
420                 rinfo.resp_count++;
421                 break;
422         case 3:
423                 *retval = rinfo.resp_error;
424                 rinfo.resp_count++;
425                 break;
426         default:
427                 remlen = rinfo.resp_size - rinfo.resp_off;
428                 dp = (uint32_t *)
429                     ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
430                 if (remlen >= sizeof(uint32_t)) {
431                         *retval = *dp;
432                 } else if (remlen > 0) {
433                         *retval = fwctl_send_rest(dp, remlen);
434                 }
435                 rinfo.resp_off += sizeof(uint32_t);
436                 break;
437         }
438
439         if (rinfo.resp_count > 3 &&
440             rinfo.resp_off >= rinfo.resp_size) {
441                 fwctl_response_done();
442                 return (1);
443         }
444
445         return (0);
446 }
447
448
449 /*
450  * i/o port handling.
451  */
452 static uint8_t
453 fwctl_inb(void)
454 {
455         uint8_t retval;
456
457         retval = 0xff;
458
459         switch (be_state) {
460         case IDENT_SEND:
461                 retval = sig[ident_idx++];
462                 if (ident_idx >= sizeof(sig))
463                         be_state = REQ;
464                 break;
465         default:
466                 break;
467         }
468
469         return (retval);
470 }
471
472 static void
473 fwctl_outw(uint16_t val)
474 {
475         switch (be_state) {
476         case IDENT_WAIT:
477                 if (val == 0) {
478                         be_state = IDENT_SEND;
479                         ident_idx = 0;
480                 }
481                 break;
482         default:
483                 /* ignore */
484                 break;
485         }
486 }
487
488 static uint32_t
489 fwctl_inl(void)
490 {
491         uint32_t retval;
492
493         switch (be_state) {
494         case RESP:
495                 if (fwctl_response(&retval))
496                         be_state = REQ;
497                 break;
498         default:
499                 retval = 0xffffffff;
500                 break;
501         }
502
503         return (retval);
504 }
505
506 static void
507 fwctl_outl(uint32_t val)
508 {
509
510         switch (be_state) {
511         case REQ:
512                 if (fwctl_request(val))
513                         be_state = RESP;
514         default:
515                 break;
516         }
517
518 }
519
520 static int
521 fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
522     uint32_t *eax, void *arg)
523 {
524
525         if (in) {
526                 if (bytes == 1)
527                         *eax = fwctl_inb();
528                 else if (bytes == 4)
529                         *eax = fwctl_inl();
530                 else
531                         *eax = 0xffff;
532         } else {
533                 if (bytes == 2)
534                         fwctl_outw(*eax);
535                 else if (bytes == 4)
536                         fwctl_outl(*eax);
537         }
538
539         return (0);
540 }
541 INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
542 INOUT_PORT(fwctl_rreg, FWCTL_IN,  IOPORT_F_IN,    fwctl_handler);
543
544 void
545 fwctl_init(void)
546 {
547
548         ops[OP_GET_LEN] = &fgetlen_info;
549         ops[OP_GET]     = &fgetval_info;
550
551         be_state = IDENT_WAIT;
552 }