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