]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/fwctl.c
Add support ps/2 scancodes for NumLock, ScrollLock and numerical keypad
[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)(int len);
83         void (*op_data)(uint32_t data, int 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(int 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, int 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(int 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, int 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         int      resp_size;
289         int      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         int remlen;
350
351         /* Make sure remaining size is >= 0 */
352         rinfo.req_size -= sizeof(uint32_t);
353         remlen = MAX(rinfo.req_size, 0);
354
355         (*rinfo.req_op->op_data)(value, remlen);
356
357         if (rinfo.req_size < sizeof(uint32_t)) {
358                 fwctl_request_done();
359                 return (1);
360         }
361
362         return (0);
363 }
364
365 static int
366 fwctl_request(uint32_t value)
367 {
368
369         int ret;
370
371         ret = 0;
372
373         switch (rinfo.req_count) {
374         case 0:
375                 /* Verify size */
376                 if (value < 12) {
377                         printf("msg size error");
378                         exit(4);
379                 }
380                 rinfo.req_size = value;
381                 rinfo.req_count = 1;
382                 break;
383         case 1:
384                 rinfo.req_type = value;
385                 rinfo.req_count++;
386                 break;
387         case 2:
388                 rinfo.req_txid = value;
389                 rinfo.req_count++;
390                 ret = fwctl_request_start();
391                 break;
392         default:
393                 ret = fwctl_request_data(value);
394                 break;
395         }
396
397         return (ret);
398 }
399
400 static int
401 fwctl_response(uint32_t *retval)
402 {
403         uint32_t *dp;
404         int remlen;
405
406         switch(rinfo.resp_count) {
407         case 0:
408                 /* 4 x u32 header len + data */
409                 *retval = 4*sizeof(uint32_t) +
410                     roundup(rinfo.resp_size, sizeof(uint32_t));
411                 rinfo.resp_count++;
412                 break;
413         case 1:
414                 *retval = rinfo.req_type;
415                 rinfo.resp_count++;
416                 break;
417         case 2:
418                 *retval = rinfo.req_txid;
419                 rinfo.resp_count++;
420                 break;
421         case 3:
422                 *retval = rinfo.resp_error;
423                 rinfo.resp_count++;
424                 break;
425         default:
426                 remlen = rinfo.resp_size - rinfo.resp_off;
427                 dp = (uint32_t *)
428                     ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
429                 if (remlen >= sizeof(uint32_t)) {
430                         *retval = *dp;
431                 } else if (remlen > 0) {
432                         *retval = fwctl_send_rest(dp, remlen);
433                 }
434                 rinfo.resp_off += sizeof(uint32_t);
435                 break;
436         }
437
438         if (rinfo.resp_count > 3 &&
439             rinfo.resp_size - rinfo.resp_off <= 0) {
440                 fwctl_response_done();
441                 return (1);
442         }
443
444         return (0);
445 }
446
447
448 /*
449  * i/o port handling.
450  */
451 static uint8_t
452 fwctl_inb(void)
453 {
454         uint8_t retval;
455
456         retval = 0xff;
457
458         switch (be_state) {
459         case IDENT_SEND:
460                 retval = sig[ident_idx++];
461                 if (ident_idx >= sizeof(sig))
462                         be_state = REQ;
463                 break;
464         default:
465                 break;
466         }
467
468         return (retval);
469 }
470
471 static void
472 fwctl_outw(uint16_t val)
473 {
474         switch (be_state) {
475         case IDENT_WAIT:
476                 if (val == 0) {
477                         be_state = IDENT_SEND;
478                         ident_idx = 0;
479                 }
480                 break;
481         default:
482                 /* ignore */
483                 break;
484         }
485 }
486
487 static uint32_t
488 fwctl_inl(void)
489 {
490         uint32_t retval;
491
492         switch (be_state) {
493         case RESP:
494                 if (fwctl_response(&retval))
495                         be_state = REQ;
496                 break;
497         default:
498                 retval = 0xffffffff;
499                 break;
500         }
501
502         return (retval);
503 }
504
505 static void
506 fwctl_outl(uint32_t val)
507 {
508
509         switch (be_state) {
510         case REQ:
511                 if (fwctl_request(val))
512                         be_state = RESP;
513         default:
514                 break;
515         }
516
517 }
518
519 static int
520 fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
521     uint32_t *eax, void *arg)
522 {
523
524         if (in) {
525                 if (bytes == 1)
526                         *eax = fwctl_inb();
527                 else if (bytes == 4)
528                         *eax = fwctl_inl();
529                 else
530                         *eax = 0xffff;
531         } else {
532                 if (bytes == 2)
533                         fwctl_outw(*eax);
534                 else if (bytes == 4)
535                         fwctl_outl(*eax);
536         }
537
538         return (0);
539 }
540 INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
541 INOUT_PORT(fwctl_rreg, FWCTL_IN,  IOPORT_F_IN,    fwctl_handler);
542
543 void
544 fwctl_init(void)
545 {
546
547         ops[OP_GET_LEN] = &fgetlen_info;
548         ops[OP_GET]     = &fgetval_info;
549
550         be_state = IDENT_WAIT;
551 }