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(uint32_t *data, size_t len)
100 cdata = (uint8_t *) data;
103 for (i = 0, u.w = 0; i < len; i++)
110 * error op dummy proto - drop all data sent and return an error
112 static int errop_code;
122 errop_start(uint32_t len)
126 /* accept any length */
131 errop_data(uint32_t data, uint32_t len)
138 errop_result(struct iovec **data)
141 /* no data to send back; always successful */
147 errop_done(struct iovec *data)
150 /* assert data is NULL */
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
161 SET_DECLARE(ctl_set, struct ctl);
163 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus));
166 ctl_locate(const char *str, int maxlen)
168 struct ctl *cp, **cpp;
170 SET_FOREACH(cpp, ctl_set) {
172 if (!strncmp(str, cp->c_oid, maxlen))
179 /* uefi-sysctl get-len */
180 #define FGET_STRSZ 80
181 static struct iovec fget_biov[2];
182 static char fget_str[FGET_STRSZ];
185 uint32_t f_data[1024];
188 static size_t fget_size;
191 fget_start(uint32_t len)
194 if (len > FGET_STRSZ)
203 fget_data(uint32_t data, uint32_t len)
206 *((uint32_t *) &fget_str[fget_cnt]) = data;
207 fget_cnt += sizeof(uint32_t);
211 fget_result(struct iovec **data, int val)
219 cp = ctl_locate(fget_str, fget_cnt);
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) +
233 fget_size = cp->c_len;
234 fget_biov[0].iov_base = (char *)&fget_size;
235 fget_biov[0].iov_len = sizeof(fget_size);
238 fget_biov[1].iov_base = NULL;
239 fget_biov[1].iov_len = 0;
247 fget_done(struct iovec *data)
250 /* nothing needs to be freed */
254 fget_len_result(struct iovec **data)
256 return (fget_result(data, 0));
260 fget_val_result(struct iovec **data)
262 return (fget_result(data, 1));
265 static struct op_info fgetlen_info = {
266 .op_start = fget_start,
267 .op_data = fget_data,
268 .op_result = fget_len_result,
272 static struct op_info fgetval_info = {
273 .op_start = fget_start,
274 .op_data = fget_data,
275 .op_result = fget_val_result,
279 static struct req_info {
285 struct op_info *req_op;
290 struct iovec *resp_biov;
294 fwctl_response_done(void)
297 (*rinfo.req_op->op_done)(rinfo.resp_biov);
299 /* reinit the req data struct */
300 memset(&rinfo, 0, sizeof(rinfo));
304 fwctl_request_done(void)
307 rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov);
309 /* XXX only a single vector supported at the moment */
311 if (rinfo.resp_biov == NULL) {
314 rinfo.resp_size = rinfo.resp_biov[0].iov_len;
319 fwctl_request_start(void)
323 /* Data size doesn't include header */
324 rinfo.req_size -= 12;
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];
330 err = (*rinfo.req_op->op_start)(rinfo.req_size);
334 rinfo.req_op = &errop_info;
337 /* Catch case of zero-length message here */
338 if (rinfo.req_size == 0) {
339 fwctl_request_done();
347 fwctl_request_data(uint32_t value)
350 /* Make sure remaining size is >= 0 */
351 if (rinfo.req_size <= sizeof(uint32_t))
354 rinfo.req_size -= sizeof(uint32_t);
356 (*rinfo.req_op->op_data)(value, rinfo.req_size);
358 if (rinfo.req_size < sizeof(uint32_t)) {
359 fwctl_request_done();
367 fwctl_request(uint32_t value)
374 switch (rinfo.req_count) {
378 printf("msg size error");
381 rinfo.req_size = value;
385 rinfo.req_type = value;
389 rinfo.req_txid = value;
391 ret = fwctl_request_start();
394 ret = fwctl_request_data(value);
402 fwctl_response(uint32_t *retval)
407 switch(rinfo.resp_count) {
409 /* 4 x u32 header len + data */
410 *retval = 4*sizeof(uint32_t) +
411 roundup(rinfo.resp_size, sizeof(uint32_t));
415 *retval = rinfo.req_type;
419 *retval = rinfo.req_txid;
423 *retval = rinfo.resp_error;
427 remlen = rinfo.resp_size - rinfo.resp_off;
429 ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off);
430 if (remlen >= sizeof(uint32_t)) {
432 } else if (remlen > 0) {
433 *retval = fwctl_send_rest(dp, remlen);
435 rinfo.resp_off += sizeof(uint32_t);
439 if (rinfo.resp_count > 3 &&
440 rinfo.resp_off >= rinfo.resp_size) {
441 fwctl_response_done();
461 retval = sig[ident_idx++];
462 if (ident_idx >= sizeof(sig))
473 fwctl_outw(uint16_t val)
478 be_state = IDENT_SEND;
495 if (fwctl_response(&retval))
507 fwctl_outl(uint32_t val)
512 if (fwctl_request(val))
521 fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
522 uint32_t *eax, void *arg)
541 INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler);
542 INOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler);
548 ops[OP_GET_LEN] = &fgetlen_info;
549 ops[OP_GET] = &fgetval_info;
551 be_state = IDENT_WAIT;