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