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 */ 69 static enum state { 70 IDENT_WAIT, 71 IDENT_SEND, 72 REQ, 73 RESP 74 } be_state; 75 76 static uint8_t sig[] = { 'B', 'H', 'Y', 'V' }; 77 static u_int ident_idx; 78 79 struct op_info { 80 int op; 81 int (*op_start)(uint32_t len); 82 void (*op_data)(uint32_t data, uint32_t len); 83 int (*op_result)(struct iovec **data); 84 void (*op_done)(struct iovec *data); 85 }; 86 static struct op_info *ops[OP_MAX+1]; 87 88 /* Return 0-padded uint32_t */ 89 static uint32_t 90 fwctl_send_rest(uint8_t *data, size_t len) 91 { 92 union { 93 uint8_t c[4]; 94 uint32_t w; 95 } u; 96 size_t i; 97 98 u.w = 0; 99 for (i = 0; i < len; i++) 100 u.c[i] = *data++; 101 102 return (u.w); 103 } 104 105 /* 106 * error op dummy proto - drop all data sent and return an error 107 */ 108 static int errop_code; 109 110 static void 111 errop_set(int err) 112 { 113 114 errop_code = err; 115 } 116 117 static int 118 errop_start(uint32_t len __unused) 119 { 120 errop_code = ENOENT; 121 122 /* accept any length */ 123 return (errop_code); 124 } 125 126 static void 127 errop_data(uint32_t data __unused, uint32_t len __unused) 128 { 129 130 /* ignore */ 131 } 132 133 static int 134 errop_result(struct iovec **data) 135 { 136 137 /* no data to send back; always successful */ 138 *data = NULL; 139 return (errop_code); 140 } 141 142 static void 143 errop_done(struct iovec *data __unused) 144 { 145 146 /* assert data is NULL */ 147 } 148 149 static struct op_info errop_info = { 150 .op_start = errop_start, 151 .op_data = errop_data, 152 .op_result = errop_result, 153 .op_done = errop_done 154 }; 155 156 /* OID search */ 157 SET_DECLARE(ctl_set, struct ctl); 158 159 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus)); 160 161 static struct ctl * 162 ctl_locate(const char *str, int maxlen) 163 { 164 struct ctl *cp, **cpp; 165 166 SET_FOREACH(cpp, ctl_set) { 167 cp = *cpp; 168 if (!strncmp(str, cp->c_oid, maxlen)) 169 return (cp); 170 } 171 172 return (NULL); 173 } 174 175 /* uefi-sysctl get-len */ 176 #define FGET_STRSZ 80 177 static struct iovec fget_biov[2]; 178 static char fget_str[FGET_STRSZ]; 179 static struct { 180 size_t f_sz; 181 uint32_t f_data[1024]; 182 } fget_buf; 183 static int fget_cnt; 184 static size_t fget_size; 185 186 static int 187 fget_start(uint32_t len) 188 { 189 190 if (len > FGET_STRSZ) 191 return(E2BIG); 192 193 fget_cnt = 0; 194 195 return (0); 196 } 197 198 static void 199 fget_data(uint32_t data, uint32_t len __unused) 200 { 201 202 assert(fget_cnt + sizeof(uint32_t) <= sizeof(fget_str)); 203 memcpy(&fget_str[fget_cnt], &data, sizeof(data)); 204 fget_cnt += sizeof(uint32_t); 205 } 206 207 static int 208 fget_result(struct iovec **data, int val) 209 { 210 struct ctl *cp; 211 int err; 212 213 err = 0; 214 215 /* Locate the OID */ 216 cp = ctl_locate(fget_str, fget_cnt); 217 if (cp == NULL) { 218 *data = NULL; 219 err = ENOENT; 220 } else { 221 if (val) { 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) + 228 cp->c_len; 229 } else { 230 fget_size = cp->c_len; 231 fget_biov[0].iov_base = (char *)&fget_size; 232 fget_biov[0].iov_len = sizeof(fget_size); 233 } 234 235 fget_biov[1].iov_base = NULL; 236 fget_biov[1].iov_len = 0; 237 *data = fget_biov; 238 } 239 240 return (err); 241 } 242 243 static void 244 fget_done(struct iovec *data __unused) 245 { 246 247 /* nothing needs to be freed */ 248 } 249 250 static int 251 fget_len_result(struct iovec **data) 252 { 253 return (fget_result(data, 0)); 254 } 255 256 static int 257 fget_val_result(struct iovec **data) 258 { 259 return (fget_result(data, 1)); 260 } 261 262 static struct op_info fgetlen_info = { 263 .op_start = fget_start, 264 .op_data = fget_data, 265 .op_result = fget_len_result, 266 .op_done = fget_done 267 }; 268 269 static struct op_info fgetval_info = { 270 .op_start = fget_start, 271 .op_data = fget_data, 272 .op_result = fget_val_result, 273 .op_done = fget_done 274 }; 275 276 static struct req_info { 277 int req_error; 278 u_int req_count; 279 uint32_t req_size; 280 uint32_t req_type; 281 uint32_t req_txid; 282 struct op_info *req_op; 283 int resp_error; 284 int resp_count; 285 size_t resp_size; 286 size_t resp_off; 287 struct iovec *resp_biov; 288 } rinfo; 289 290 static void 291 fwctl_response_done(void) 292 { 293 294 (*rinfo.req_op->op_done)(rinfo.resp_biov); 295 296 /* reinit the req data struct */ 297 memset(&rinfo, 0, sizeof(rinfo)); 298 } 299 300 static void 301 fwctl_request_done(void) 302 { 303 304 rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov); 305 306 /* XXX only a single vector supported at the moment */ 307 rinfo.resp_off = 0; 308 if (rinfo.resp_biov == NULL) { 309 rinfo.resp_size = 0; 310 } else { 311 rinfo.resp_size = rinfo.resp_biov[0].iov_len; 312 } 313 } 314 315 static int 316 fwctl_request_start(void) 317 { 318 int err; 319 320 /* Data size doesn't include header */ 321 rinfo.req_size -= 12; 322 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]; 326 327 err = (*rinfo.req_op->op_start)(rinfo.req_size); 328 329 if (err) { 330 errop_set(err); 331 rinfo.req_op = &errop_info; 332 } 333 334 /* Catch case of zero-length message here */ 335 if (rinfo.req_size == 0) { 336 fwctl_request_done(); 337 return (1); 338 } 339 340 return (0); 341 } 342 343 static int 344 fwctl_request_data(uint32_t value) 345 { 346 347 /* Make sure remaining size is > 0 */ 348 assert(rinfo.req_size > 0); 349 if (rinfo.req_size <= sizeof(uint32_t)) 350 rinfo.req_size = 0; 351 else 352 rinfo.req_size -= sizeof(uint32_t); 353 354 (*rinfo.req_op->op_data)(value, rinfo.req_size); 355 356 if (rinfo.req_size < sizeof(uint32_t)) { 357 fwctl_request_done(); 358 return (1); 359 } 360 361 return (0); 362 } 363 364 static int 365 fwctl_request(uint32_t value) 366 { 367 368 int ret; 369 370 ret = 0; 371 372 switch (rinfo.req_count) { 373 case 0: 374 /* Verify size */ 375 if (value < 12) { 376 printf("msg size error"); 377 exit(4); 378 } 379 rinfo.req_size = value; 380 rinfo.req_count = 1; 381 break; 382 case 1: 383 rinfo.req_type = value; 384 rinfo.req_count++; 385 break; 386 case 2: 387 rinfo.req_txid = value; 388 rinfo.req_count++; 389 ret = fwctl_request_start(); 390 break; 391 default: 392 ret = fwctl_request_data(value); 393 break; 394 } 395 396 return (ret); 397 } 398 399 static int 400 fwctl_response(uint32_t *retval) 401 { 402 uint8_t *dp; 403 ssize_t remlen; 404 405 switch(rinfo.resp_count) { 406 case 0: 407 /* 4 x u32 header len + data */ 408 *retval = 4*sizeof(uint32_t) + 409 roundup(rinfo.resp_size, sizeof(uint32_t)); 410 rinfo.resp_count++; 411 break; 412 case 1: 413 *retval = rinfo.req_type; 414 rinfo.resp_count++; 415 break; 416 case 2: 417 *retval = rinfo.req_txid; 418 rinfo.resp_count++; 419 break; 420 case 3: 421 *retval = rinfo.resp_error; 422 rinfo.resp_count++; 423 break; 424 default: 425 remlen = rinfo.resp_size - rinfo.resp_off; 426 dp = (uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off; 427 if (remlen >= (ssize_t)sizeof(uint32_t)) { 428 memcpy(retval, dp, sizeof(uint32_t)); 429 } else if (remlen > 0) { 430 *retval = fwctl_send_rest(dp, remlen); 431 } 432 rinfo.resp_off += sizeof(uint32_t); 433 break; 434 } 435 436 if (rinfo.resp_count > 3 && 437 rinfo.resp_off >= rinfo.resp_size) { 438 fwctl_response_done(); 439 return (1); 440 } 441 442 return (0); 443 } 444 445 static void 446 fwctl_reset(void) 447 { 448 449 switch (be_state) { 450 case RESP: 451 /* If a response was generated but not fully read, discard it. */ 452 fwctl_response_done(); 453 break; 454 case REQ: 455 /* Discard partially-received request. */ 456 memset(&rinfo, 0, sizeof(rinfo)); 457 break; 458 case IDENT_WAIT: 459 case IDENT_SEND: 460 break; 461 } 462 463 be_state = IDENT_SEND; 464 ident_idx = 0; 465 } 466 467 468 /* 469 * i/o port handling. 470 */ 471 static uint8_t 472 fwctl_inb(void) 473 { 474 uint8_t retval; 475 476 retval = 0xff; 477 478 switch (be_state) { 479 case IDENT_SEND: 480 retval = sig[ident_idx++]; 481 if (ident_idx >= sizeof(sig)) 482 be_state = REQ; 483 break; 484 default: 485 break; 486 } 487 488 return (retval); 489 } 490 491 static void 492 fwctl_outw(uint16_t val) 493 { 494 if (val == 0) { 495 /* 496 * The guest wants to read the signature. It's possible that the 497 * guest is unaware of the fwctl state at this moment. For that 498 * reason, reset the state machine unconditionally. 499 */ 500 fwctl_reset(); 501 } 502 } 503 504 static uint32_t 505 fwctl_inl(void) 506 { 507 uint32_t retval; 508 509 switch (be_state) { 510 case RESP: 511 if (fwctl_response(&retval)) 512 be_state = REQ; 513 break; 514 default: 515 retval = 0xffffffff; 516 break; 517 } 518 519 return (retval); 520 } 521 522 static void 523 fwctl_outl(uint32_t val) 524 { 525 526 switch (be_state) { 527 case REQ: 528 if (fwctl_request(val)) 529 be_state = RESP; 530 default: 531 break; 532 } 533 534 } 535 536 static int 537 fwctl_handler(struct vmctx *ctx __unused, int in, 538 int port __unused, int bytes, uint32_t *eax, void *arg __unused) 539 { 540 541 if (in) { 542 if (bytes == 1) 543 *eax = fwctl_inb(); 544 else if (bytes == 4) 545 *eax = fwctl_inl(); 546 else 547 *eax = 0xffff; 548 } else { 549 if (bytes == 2) 550 fwctl_outw(*eax); 551 else if (bytes == 4) 552 fwctl_outl(*eax); 553 } 554 555 return (0); 556 } 557 558 void 559 fwctl_init(void) 560 { 561 struct inout_port iop; 562 int error; 563 564 bzero(&iop, sizeof(iop)); 565 iop.name = "fwctl_wreg"; 566 iop.port = FWCTL_OUT; 567 iop.size = 1; 568 iop.flags = IOPORT_F_INOUT; 569 iop.handler = fwctl_handler; 570 571 error = register_inout(&iop); 572 assert(error == 0); 573 574 bzero(&iop, sizeof(iop)); 575 iop.name = "fwctl_rreg"; 576 iop.port = FWCTL_IN; 577 iop.size = 1; 578 iop.flags = IOPORT_F_IN; 579 iop.handler = fwctl_handler; 580 581 error = register_inout(&iop); 582 assert(error == 0); 583 584 ops[OP_GET_LEN] = &fgetlen_info; 585 ops[OP_GET] = &fgetval_info; 586 587 be_state = IDENT_WAIT; 588 } 589