1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 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 */ 65static enum state { 66 IDENT, 67 REQ, 68 RESP 69} be_state; 70 71static uint8_t sig[] = { 'B', 'H', 'Y', 'V' }; 72static u_int ident_idx; 73 74struct 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}; 81static struct op_info *ops[OP_MAX+1]; 82 83/* Return 0-padded uint32_t */ 84static uint32_t 85fwctl_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*/ 103static int errop_code; 104 105static void 106errop_set(int err) 107{ 108 109 errop_code = err; 110} 111 112static int 113errop_start(uint32_t len __unused) 114{ 115 errop_code = ENOENT; 116 117 /* accept any length */ 118 return (errop_code); 119} 120 121static void 122errop_data(uint32_t data __unused) 123{ 124 125 /* ignore */ 126} 127 128static int 129errop_result(struct iovec **data) 130{ 131 132 /* no data to send back; always successful */ 133 *data = NULL; 134 return (errop_code); 135} 136 137static void 138errop_done(struct iovec *data __unused) 139{ 140 141 /* assert data is NULL */ 142} 143 144static 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 */ 152SET_DECLARE(ctl_set, struct ctl); 153 154CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus)); 155 156static struct ctl * 157ctl_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 172static struct iovec fget_biov[2]; 173static char fget_str[FGET_STRSZ]; 174static struct { 175 size_t f_sz; 176 uint32_t f_data[1024]; 177} fget_buf; 178static int fget_cnt; 179static size_t fget_size; 180 181static int 182fget_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 193static void 194fget_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 202static int 203fget_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 238static void 239fget_done(struct iovec *data __unused) 240{ 241 242 /* nothing needs to be freed */ 243} 244 245static int 246fget_len_result(struct iovec **data) 247{ 248 return (fget_result(data, 0)); 249} 250 251static int 252fget_val_result(struct iovec **data) 253{ 254 return (fget_result(data, 1)); 255} 256 257static 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 264static 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 271static 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 285static void 286fwctl_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 295static void 296fwctl_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 310static int 311fwctl_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 338static int 339fwctl_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 359static int 360fwctl_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(4); 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 393static int 394fwctl_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 439static void 440fwctl_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 */ 464static uint8_t 465fwctl_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 484static void 485fwctl_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 497static uint32_t 498fwctl_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 515static void 516fwctl_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 529static int 530fwctl_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 551void 552fwctl_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