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