2 * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does,
31 * but with a request/response messaging protocol.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
51 * Messaging protocol base operations
61 #define FWCTL_OUT 0x510
62 #define FWCTL_IN 0x511
65 * Back-end state-machine
75 static uint8_t sig
[] = { 'B', 'H', 'Y', 'V' };
76 static u_int ident_idx
;
80 int (*op_start
)(int len
);
81 void (*op_data
)(uint32_t data
, int len
);
82 int (*op_result
)(struct iovec
**data
);
83 void (*op_done
)(struct iovec
*data
);
85 static struct op_info
*ops
[OP_MAX
+1];
87 /* Return 0-padded uint32_t */
89 fwctl_send_rest(uint32_t *data
, size_t len
)
98 cdata
= (uint8_t *) data
;
101 for (i
= 0, u
.w
= 0; i
< len
; i
++)
108 * error op dummy proto - drop all data sent and return an error
110 static int errop_code
;
124 /* accept any length */
129 errop_data(uint32_t data
, int len
)
136 errop_result(struct iovec
**data
)
139 /* no data to send back; always successful */
145 errop_done(struct iovec
*data
)
148 /* assert data is NULL */
151 static struct op_info errop_info
= {
152 .op_start
= errop_start
,
153 .op_data
= errop_data
,
154 .op_result
= errop_result
,
155 .op_done
= errop_done
159 SET_DECLARE(ctl_set
, struct ctl
);
161 CTL_NODE("hw.ncpu", &guest_ncpus
, sizeof(guest_ncpus
));
164 ctl_locate(const char *str
, int maxlen
)
166 struct ctl
*cp
, **cpp
;
168 SET_FOREACH(cpp
, ctl_set
) {
170 if (!strncmp(str
, cp
->c_oid
, maxlen
))
177 /* uefi-sysctl get-len */
178 #define FGET_STRSZ 80
179 static struct iovec fget_biov
[2];
180 static char fget_str
[FGET_STRSZ
];
183 uint32_t f_data
[1024];
186 static size_t fget_size
;
192 if (len
> FGET_STRSZ
)
201 fget_data(uint32_t data
, int len
)
204 *((uint32_t *) &fget_str
[fget_cnt
]) = data
;
205 fget_cnt
+= sizeof(uint32_t);
209 fget_result(struct iovec
**data
, int val
)
217 cp
= ctl_locate(fget_str
, fget_cnt
);
223 /* For now, copy the len/data into a buffer */
224 memset(&fget_buf
, 0, sizeof(fget_buf
));
225 fget_buf
.f_sz
= cp
->c_len
;
226 memcpy(fget_buf
.f_data
, cp
->c_data
, cp
->c_len
);
227 fget_biov
[0].iov_base
= (char *)&fget_buf
;
228 fget_biov
[0].iov_len
= sizeof(fget_buf
.f_sz
) +
231 fget_size
= cp
->c_len
;
232 fget_biov
[0].iov_base
= (char *)&fget_size
;
233 fget_biov
[0].iov_len
= sizeof(fget_size
);
236 fget_biov
[1].iov_base
= NULL
;
237 fget_biov
[1].iov_len
= 0;
245 fget_done(struct iovec
*data
)
248 /* nothing needs to be freed */
252 fget_len_result(struct iovec
**data
)
254 return (fget_result(data
, 0));
258 fget_val_result(struct iovec
**data
)
260 return (fget_result(data
, 1));
263 static struct op_info fgetlen_info
= {
264 .op_start
= fget_start
,
265 .op_data
= fget_data
,
266 .op_result
= fget_len_result
,
270 static struct op_info fgetval_info
= {
271 .op_start
= fget_start
,
272 .op_data
= fget_data
,
273 .op_result
= fget_val_result
,
277 static struct req_info
{
283 struct op_info
*req_op
;
288 struct iovec
*resp_biov
;
292 fwctl_response_done(void)
295 (*rinfo
.req_op
->op_done
)(rinfo
.resp_biov
);
297 /* reinit the req data struct */
298 memset(&rinfo
, 0, sizeof(rinfo
));
302 fwctl_request_done(void)
305 rinfo
.resp_error
= (*rinfo
.req_op
->op_result
)(&rinfo
.resp_biov
);
307 /* XXX only a single vector supported at the moment */
309 if (rinfo
.resp_biov
== NULL
) {
312 rinfo
.resp_size
= rinfo
.resp_biov
[0].iov_len
;
317 fwctl_request_start(void)
321 /* Data size doesn't include header */
322 rinfo
.req_size
-= 12;
324 rinfo
.req_op
= &errop_info
;
325 if (rinfo
.req_type
<= OP_MAX
&& ops
[rinfo
.req_type
] != NULL
)
326 rinfo
.req_op
= ops
[rinfo
.req_type
];
328 err
= (*rinfo
.req_op
->op_start
)(rinfo
.req_size
);
332 rinfo
.req_op
= &errop_info
;
335 /* Catch case of zero-length message here */
336 if (rinfo
.req_size
== 0) {
337 fwctl_request_done();
345 fwctl_request_data(uint32_t value
)
349 /* Make sure remaining size is >= 0 */
350 rinfo
.req_size
-= sizeof(uint32_t);
351 remlen
= (rinfo
.req_size
> 0) ? rinfo
.req_size
: 0;
353 (*rinfo
.req_op
->op_data
)(value
, remlen
);
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
;
426 ((uint8_t *)rinfo
.resp_biov
->iov_base
+ rinfo
.resp_off
);
427 if (remlen
>= sizeof(uint32_t)) {
429 } else if (remlen
> 0) {
430 *retval
= fwctl_send_rest(dp
, remlen
);
432 rinfo
.resp_off
+= sizeof(uint32_t);
436 if (rinfo
.resp_count
> 3 &&
437 rinfo
.resp_size
- rinfo
.resp_off
<= 0) {
438 fwctl_response_done();
458 retval
= sig
[ident_idx
++];
459 if (ident_idx
>= sizeof(sig
))
470 fwctl_outw(uint16_t val
)
475 be_state
= IDENT_SEND
;
492 if (fwctl_response(&retval
))
504 fwctl_outl(uint32_t val
)
509 if (fwctl_request(val
))
518 fwctl_handler(struct vmctx
*ctx
, int vcpu
, int in
, int port
, int bytes
,
519 uint32_t *eax
, void *arg
)
538 INOUT_PORT(fwctl_wreg
, FWCTL_OUT
, IOPORT_F_INOUT
, fwctl_handler
);
539 INOUT_PORT(fwctl_rreg
, FWCTL_IN
, IOPORT_F_IN
, fwctl_handler
);
545 ops
[OP_GET_LEN
] = &fgetlen_info
;
546 ops
[OP_GET
] = &fgetval_info
;
548 be_state
= IDENT_WAIT
;