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