2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
33 * but with a request/response messaging protocol.
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/errno.h>
53 * Messaging protocol base operations
63 #define FWCTL_OUT 0x510
64 #define FWCTL_IN 0x511
67 * Back-end state-machine
77 static uint8_t sig
[] = { 'B', 'H', 'Y', 'V' };
78 static u_int ident_idx
;
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
);
87 static struct op_info
*ops
[OP_MAX
+1];
89 /* Return 0-padded uint32_t */
91 fwctl_send_rest(uint8_t *data
, size_t len
)
100 for (i
= 0; i
< len
; i
++)
107 * error op dummy proto - drop all data sent and return an error
109 static int errop_code
;
119 errop_start(uint32_t len __unused
)
123 /* accept any length */
128 errop_data(uint32_t data __unused
, uint32_t len __unused
)
135 errop_result(struct iovec
**data
)
138 /* no data to send back; always successful */
144 errop_done(struct iovec
*data __unused
)
147 /* assert data is NULL */
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
158 SET_DECLARE(ctl_set
, struct ctl
);
160 CTL_NODE("hw.ncpu", &guest_ncpus
, sizeof(guest_ncpus
));
163 ctl_locate(const char *str
, int maxlen
)
165 struct ctl
*cp
, **cpp
;
167 SET_FOREACH(cpp
, ctl_set
) {
169 if (!strncmp(str
, cp
->c_oid
, maxlen
))
176 /* uefi-sysctl get-len */
177 #define FGET_STRSZ 80
178 static struct iovec fget_biov
[2];
179 static char fget_str
[FGET_STRSZ
];
182 uint32_t f_data
[1024];
185 static size_t fget_size
;
188 fget_start(uint32_t len
)
191 if (len
> FGET_STRSZ
)
200 fget_data(uint32_t data
, uint32_t len __unused
)
203 memcpy(&fget_str
[fget_cnt
], &data
, sizeof(data
));
204 fget_cnt
+= sizeof(uint32_t);
208 fget_result(struct iovec
**data
, int val
)
216 cp
= ctl_locate(fget_str
, fget_cnt
);
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
) +
230 fget_size
= cp
->c_len
;
231 fget_biov
[0].iov_base
= (char *)&fget_size
;
232 fget_biov
[0].iov_len
= sizeof(fget_size
);
235 fget_biov
[1].iov_base
= NULL
;
236 fget_biov
[1].iov_len
= 0;
244 fget_done(struct iovec
*data __unused
)
247 /* nothing needs to be freed */
251 fget_len_result(struct iovec
**data
)
253 return (fget_result(data
, 0));
257 fget_val_result(struct iovec
**data
)
259 return (fget_result(data
, 1));
262 static struct op_info fgetlen_info
= {
263 .op_start
= fget_start
,
264 .op_data
= fget_data
,
265 .op_result
= fget_len_result
,
269 static struct op_info fgetval_info
= {
270 .op_start
= fget_start
,
271 .op_data
= fget_data
,
272 .op_result
= fget_val_result
,
276 static struct req_info
{
282 struct op_info
*req_op
;
287 struct iovec
*resp_biov
;
291 fwctl_response_done(void)
294 (*rinfo
.req_op
->op_done
)(rinfo
.resp_biov
);
296 /* reinit the req data struct */
297 memset(&rinfo
, 0, sizeof(rinfo
));
301 fwctl_request_done(void)
304 rinfo
.resp_error
= (*rinfo
.req_op
->op_result
)(&rinfo
.resp_biov
);
306 /* XXX only a single vector supported at the moment */
308 if (rinfo
.resp_biov
== NULL
) {
311 rinfo
.resp_size
= rinfo
.resp_biov
[0].iov_len
;
316 fwctl_request_start(void)
320 /* Data size doesn't include header */
321 rinfo
.req_size
-= 12;
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
];
327 err
= (*rinfo
.req_op
->op_start
)(rinfo
.req_size
);
331 rinfo
.req_op
= &errop_info
;
334 /* Catch case of zero-length message here */
335 if (rinfo
.req_size
== 0) {
336 fwctl_request_done();
344 fwctl_request_data(uint32_t value
)
347 /* Make sure remaining size is >= 0 */
348 if (rinfo
.req_size
<= sizeof(uint32_t))
351 rinfo
.req_size
-= sizeof(uint32_t);
353 (*rinfo
.req_op
->op_data
)(value
, rinfo
.req_size
);
355 if (rinfo
.req_size
< sizeof(uint32_t)) {
356 fwctl_request_done();
364 fwctl_request(uint32_t value
)
371 switch (rinfo
.req_count
) {
375 printf("msg size error");
378 rinfo
.req_size
= value
;
382 rinfo
.req_type
= value
;
386 rinfo
.req_txid
= value
;
388 ret
= fwctl_request_start();
391 ret
= fwctl_request_data(value
);
399 fwctl_response(uint32_t *retval
)
404 switch(rinfo
.resp_count
) {
406 /* 4 x u32 header len + data */
407 *retval
= 4*sizeof(uint32_t) +
408 roundup(rinfo
.resp_size
, sizeof(uint32_t));
412 *retval
= rinfo
.req_type
;
416 *retval
= rinfo
.req_txid
;
420 *retval
= rinfo
.resp_error
;
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
);
431 rinfo
.resp_off
+= sizeof(uint32_t);
435 if (rinfo
.resp_count
> 3 &&
436 rinfo
.resp_off
>= rinfo
.resp_size
) {
437 fwctl_response_done();
457 retval
= sig
[ident_idx
++];
458 if (ident_idx
>= sizeof(sig
))
469 fwctl_outw(uint16_t val
)
471 if (be_state
== DORMANT
) {
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.
481 be_state
= IDENT_SEND
;
493 if (fwctl_response(&retval
))
505 fwctl_outl(uint32_t val
)
510 if (fwctl_request(val
))
519 fwctl_handler(struct vmctx
*ctx __unused
, int in
,
520 int port __unused
, int bytes
, uint32_t *eax
, void *arg __unused
)
543 struct inout_port iop
;
546 bzero(&iop
, sizeof(iop
));
547 iop
.name
= "fwctl_wreg";
548 iop
.port
= FWCTL_OUT
;
550 iop
.flags
= IOPORT_F_INOUT
;
551 iop
.handler
= fwctl_handler
;
553 error
= register_inout(&iop
);
556 bzero(&iop
, sizeof(iop
));
557 iop
.name
= "fwctl_rreg";
560 iop
.flags
= IOPORT_F_IN
;
561 iop
.handler
= fwctl_handler
;
563 error
= register_inout(&iop
);
566 ops
[OP_GET_LEN
] = &fgetlen_info
;
567 ops
[OP_GET
] = &fgetval_info
;
569 be_state
= IDENT_WAIT
;