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 */ 69enum state { 70 DORMANT, 71 IDENT_WAIT, 72 IDENT_SEND, 73 REQ, 74 RESP 75} be_state = DORMANT; 76 77static uint8_t sig[] = { 'B', 'H', 'Y', 'V' }; 78static u_int ident_idx; 79 80struct op_info { 81 int op; 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); 86}; 87static struct op_info *ops[OP_MAX+1]; 88 89/* Return 0-padded uint32_t */ 90static uint32_t 91fwctl_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*/ 112static int errop_code; 113 114static void 115errop_set(int err) 116{ 117 118 errop_code = err; 119} 120 121static int 122errop_start(uint32_t len) 123{ 124 errop_code = ENOENT; 125 126 /* accept any length */ 127 return (errop_code); 128} 129 130static void 131errop_data(uint32_t data, uint32_t len) 132{ 133 134 /* ignore */ 135} 136 137static int 138errop_result(struct iovec **data) 139{ 140 141 /* no data to send back; always successful */ 142 *data = NULL; 143 return (errop_code); 144} 145 146static void 147errop_done(struct iovec *data) 148{ 149 150 /* assert data is NULL */ 151} 152 153static 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 */ 161SET_DECLARE(ctl_set, struct ctl); 162 163CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus)); 164 165static struct ctl * 166ctl_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 181static struct iovec fget_biov[2]; 182static char fget_str[FGET_STRSZ]; 183static struct { 184 size_t f_sz; 185 uint32_t f_data[1024]; 186} fget_buf; 187static int fget_cnt; 188static size_t fget_size; 189 190static int 191fget_start(uint32_t len) 192{ 193 194 if (len > FGET_STRSZ) 195 return(E2BIG); 196 197 fget_cnt = 0; 198 199 return (0); 200} 201 202static void 203fget_data(uint32_t data, uint32_t len) 204{ 205 206 *((uint32_t *) &fget_str[fget_cnt]) = data; 207 fget_cnt += sizeof(uint32_t); 208} 209 210static int 211fget_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 246static void 247fget_done(struct iovec *data) 248{ 249 250 /* nothing needs to be freed */ 251} 252 253static int 254fget_len_result(struct iovec **data) 255{ 256 return (fget_result(data, 0)); 257} 258 259static int 260fget_val_result(struct iovec **data) 261{ 262 return (fget_result(data, 1)); 263} 264 265static 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 272static 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 279static 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 size_t resp_size; 289 size_t resp_off; 290 struct iovec *resp_biov; 291} rinfo; 292 293static void 294fwctl_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 303static void 304fwctl_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 318static int 319fwctl_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 346static int 347fwctl_request_data(uint32_t value) 348{ 349 350 /* Make sure remaining size is >= 0 */ 351 if (rinfo.req_size <= sizeof(uint32_t)) 352 rinfo.req_size = 0; 353 else 354 rinfo.req_size -= sizeof(uint32_t); 355 356 (*rinfo.req_op->op_data)(value, rinfo.req_size); 357 358 if (rinfo.req_size < sizeof(uint32_t)) { 359 fwctl_request_done(); 360 return (1); 361 } 362 363 return (0); 364} 365 366static int 367fwctl_request(uint32_t value) 368{ 369 370 int ret; 371 372 ret = 0; 373 374 switch (rinfo.req_count) { 375 case 0: 376 /* Verify size */ 377 if (value < 12) { 378 printf("msg size error"); 379 exit(4); 380 } 381 rinfo.req_size = value; 382 rinfo.req_count = 1; 383 break; 384 case 1: 385 rinfo.req_type = value; 386 rinfo.req_count++; 387 break; 388 case 2: 389 rinfo.req_txid = value; 390 rinfo.req_count++; 391 ret = fwctl_request_start(); 392 break; 393 default: 394 ret = fwctl_request_data(value); 395 break; 396 } 397 398 return (ret); 399} 400 401static int 402fwctl_response(uint32_t *retval) 403{ 404 uint32_t *dp; 405 ssize_t remlen; 406 407 switch(rinfo.resp_count) { 408 case 0: 409 /* 4 x u32 header len + data */ 410 *retval = 4*sizeof(uint32_t) + 411 roundup(rinfo.resp_size, sizeof(uint32_t)); 412 rinfo.resp_count++; 413 break; 414 case 1: 415 *retval = rinfo.req_type; 416 rinfo.resp_count++; 417 break; 418 case 2: 419 *retval = rinfo.req_txid; 420 rinfo.resp_count++; 421 break; 422 case 3: 423 *retval = rinfo.resp_error; 424 rinfo.resp_count++; 425 break; 426 default: 427 remlen = rinfo.resp_size - rinfo.resp_off; 428 dp = (uint32_t *) 429 ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off); 430 if (remlen >= sizeof(uint32_t)) { 431 *retval = *dp; 432 } else if (remlen > 0) { 433 *retval = fwctl_send_rest(dp, remlen); 434 } 435 rinfo.resp_off += sizeof(uint32_t); 436 break; 437 } 438 439 if (rinfo.resp_count > 3 && 440 rinfo.resp_off >= rinfo.resp_size) { 441 fwctl_response_done(); 442 return (1); 443 } 444 445 return (0); 446} 447 448 449/* 450 * i/o port handling. 451 */ 452static uint8_t 453fwctl_inb(void) 454{ 455 uint8_t retval; 456 457 retval = 0xff; 458 459 switch (be_state) { 460 case IDENT_SEND: 461 retval = sig[ident_idx++]; 462 if (ident_idx >= sizeof(sig)) 463 be_state = REQ; 464 break; 465 default: 466 break; 467 } 468 469 return (retval); 470} 471 472static void 473fwctl_outw(uint16_t val) 474{ 475 switch (be_state) { 476 case IDENT_WAIT: 477 if (val == 0) { 478 be_state = IDENT_SEND; 479 ident_idx = 0; 480 } 481 break; 482 default: 483 /* ignore */ 484 break; 485 } 486} 487 488static uint32_t 489fwctl_inl(void) 490{ 491 uint32_t retval; 492 493 switch (be_state) { 494 case RESP: 495 if (fwctl_response(&retval)) 496 be_state = REQ; 497 break; 498 default: 499 retval = 0xffffffff; 500 break; 501 } 502 503 return (retval); 504} 505 506static void 507fwctl_outl(uint32_t val) 508{ 509 510 switch (be_state) { 511 case REQ: 512 if (fwctl_request(val)) 513 be_state = RESP; 514 default: 515 break; 516 } 517 518} 519 520static int 521fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 522 uint32_t *eax, void *arg) 523{ 524 525 if (in) { 526 if (bytes == 1) 527 *eax = fwctl_inb(); 528 else if (bytes == 4) 529 *eax = fwctl_inl(); 530 else 531 *eax = 0xffff; 532 } else { 533 if (bytes == 2) 534 fwctl_outw(*eax); 535 else if (bytes == 4) 536 fwctl_outl(*eax); 537 } 538 539 return (0); 540} 541INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler); 542INOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler); 543 544void 545fwctl_init(void) 546{ 547 548 ops[OP_GET_LEN] = &fgetlen_info; 549 ops[OP_GET] = &fgetval_info; 550 551 be_state = IDENT_WAIT; 552} 553