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 DORMANT, 71 IDENT_WAIT, 72 IDENT_SEND, 73 REQ, 74 RESP 75 } be_state = DORMANT; 76 77 static uint8_t sig[] = { 'B', 'H', 'Y', 'V' }; 78 static u_int ident_idx; 79 80 struct op_info { 81 int op; 82 int (*op_start)(uint32_t len); 83 void (*op_data)(uint32_t data, uint32_t len); 84 int (*op_result)(struct iovec **data); 85 void (*op_done)(struct iovec *data); 86 }; 87 static struct op_info *ops[OP_MAX+1]; 88 89 /* Return 0-padded uint32_t */ 90 static uint32_t 91 fwctl_send_rest(uint8_t *data, size_t len) 92 { 93 union { 94 uint8_t c[4]; 95 uint32_t w; 96 } u; 97 size_t i; 98 99 u.w = 0; 100 for (i = 0; i < len; i++) 101 u.c[i] = *data++; 102 103 return (u.w); 104 } 105 106 /* 107 * error op dummy proto - drop all data sent and return an error 108 */ 109 static int errop_code; 110 111 static void 112 errop_set(int err) 113 { 114 115 errop_code = err; 116 } 117 118 static int 119 errop_start(uint32_t len __unused) 120 { 121 errop_code = ENOENT; 122 123 /* accept any length */ 124 return (errop_code); 125 } 126 127 static void 128 errop_data(uint32_t data __unused, uint32_t len __unused) 129 { 130 131 /* ignore */ 132 } 133 134 static int 135 errop_result(struct iovec **data) 136 { 137 138 /* no data to send back; always successful */ 139 *data = NULL; 140 return (errop_code); 141 } 142 143 static void 144 errop_done(struct iovec *data __unused) 145 { 146 147 /* assert data is NULL */ 148 } 149 150 static struct op_info errop_info = { 151 .op_start = errop_start, 152 .op_data = errop_data, 153 .op_result = errop_result, 154 .op_done = errop_done 155 }; 156 157 /* OID search */ 158 SET_DECLARE(ctl_set, struct ctl); 159 160 CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus)); 161 162 static struct ctl * 163 ctl_locate(const char *str, int maxlen) 164 { 165 struct ctl *cp, **cpp; 166 167 SET_FOREACH(cpp, ctl_set) { 168 cp = *cpp; 169 if (!strncmp(str, cp->c_oid, maxlen)) 170 return (cp); 171 } 172 173 return (NULL); 174 } 175 176 /* uefi-sysctl get-len */ 177 #define FGET_STRSZ 80 178 static struct iovec fget_biov[2]; 179 static char fget_str[FGET_STRSZ]; 180 static struct { 181 size_t f_sz; 182 uint32_t f_data[1024]; 183 } fget_buf; 184 static int fget_cnt; 185 static size_t fget_size; 186 187 static int 188 fget_start(uint32_t len) 189 { 190 191 if (len > FGET_STRSZ) 192 return(E2BIG); 193 194 fget_cnt = 0; 195 196 return (0); 197 } 198 199 static void 200 fget_data(uint32_t data, uint32_t len __unused) 201 { 202 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 if (rinfo.req_size <= sizeof(uint32_t)) 349 rinfo.req_size = 0; 350 else 351 rinfo.req_size -= sizeof(uint32_t); 352 353 (*rinfo.req_op->op_data)(value, rinfo.req_size); 354 355 if (rinfo.req_size < sizeof(uint32_t)) { 356 fwctl_request_done(); 357 return (1); 358 } 359 360 return (0); 361 } 362 363 static int 364 fwctl_request(uint32_t value) 365 { 366 367 int ret; 368 369 ret = 0; 370 371 switch (rinfo.req_count) { 372 case 0: 373 /* Verify size */ 374 if (value < 12) { 375 printf("msg size error"); 376 exit(4); 377 } 378 rinfo.req_size = value; 379 rinfo.req_count = 1; 380 break; 381 case 1: 382 rinfo.req_type = value; 383 rinfo.req_count++; 384 break; 385 case 2: 386 rinfo.req_txid = value; 387 rinfo.req_count++; 388 ret = fwctl_request_start(); 389 break; 390 default: 391 ret = fwctl_request_data(value); 392 break; 393 } 394 395 return (ret); 396 } 397 398 static int 399 fwctl_response(uint32_t *retval) 400 { 401 uint8_t *dp; 402 ssize_t remlen; 403 404 switch(rinfo.resp_count) { 405 case 0: 406 /* 4 x u32 header len + data */ 407 *retval = 4*sizeof(uint32_t) + 408 roundup(rinfo.resp_size, sizeof(uint32_t)); 409 rinfo.resp_count++; 410 break; 411 case 1: 412 *retval = rinfo.req_type; 413 rinfo.resp_count++; 414 break; 415 case 2: 416 *retval = rinfo.req_txid; 417 rinfo.resp_count++; 418 break; 419 case 3: 420 *retval = rinfo.resp_error; 421 rinfo.resp_count++; 422 break; 423 default: 424 remlen = rinfo.resp_size - rinfo.resp_off; 425 dp = (uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off; 426 if (remlen >= (ssize_t)sizeof(uint32_t)) { 427 memcpy(retval, dp, sizeof(uint32_t)); 428 } else if (remlen > 0) { 429 *retval = fwctl_send_rest(dp, remlen); 430 } 431 rinfo.resp_off += sizeof(uint32_t); 432 break; 433 } 434 435 if (rinfo.resp_count > 3 && 436 rinfo.resp_off >= rinfo.resp_size) { 437 fwctl_response_done(); 438 return (1); 439 } 440 441 return (0); 442 } 443 444 445 /* 446 * i/o port handling. 447 */ 448 static uint8_t 449 fwctl_inb(void) 450 { 451 uint8_t retval; 452 453 retval = 0xff; 454 455 switch (be_state) { 456 case IDENT_SEND: 457 retval = sig[ident_idx++]; 458 if (ident_idx >= sizeof(sig)) 459 be_state = REQ; 460 break; 461 default: 462 break; 463 } 464 465 return (retval); 466 } 467 468 static void 469 fwctl_outw(uint16_t val) 470 { 471 if (be_state == DORMANT) { 472 return; 473 } 474 475 if (val == 0) { 476 /* 477 * The guest wants to read the signature. It's possible that the 478 * guest is unaware of the fwctl state at this moment. For that 479 * reason, reset the state machine unconditionally. 480 */ 481 be_state = IDENT_SEND; 482 ident_idx = 0; 483 } 484 } 485 486 static uint32_t 487 fwctl_inl(void) 488 { 489 uint32_t retval; 490 491 switch (be_state) { 492 case RESP: 493 if (fwctl_response(&retval)) 494 be_state = REQ; 495 break; 496 default: 497 retval = 0xffffffff; 498 break; 499 } 500 501 return (retval); 502 } 503 504 static void 505 fwctl_outl(uint32_t val) 506 { 507 508 switch (be_state) { 509 case REQ: 510 if (fwctl_request(val)) 511 be_state = RESP; 512 default: 513 break; 514 } 515 516 } 517 518 static int 519 fwctl_handler(struct vmctx *ctx __unused, int in, 520 int port __unused, int bytes, uint32_t *eax, void *arg __unused) 521 { 522 523 if (in) { 524 if (bytes == 1) 525 *eax = fwctl_inb(); 526 else if (bytes == 4) 527 *eax = fwctl_inl(); 528 else 529 *eax = 0xffff; 530 } else { 531 if (bytes == 2) 532 fwctl_outw(*eax); 533 else if (bytes == 4) 534 fwctl_outl(*eax); 535 } 536 537 return (0); 538 } 539 540 void 541 fwctl_init(void) 542 { 543 struct inout_port iop; 544 int error; 545 546 bzero(&iop, sizeof(iop)); 547 iop.name = "fwctl_wreg"; 548 iop.port = FWCTL_OUT; 549 iop.size = 1; 550 iop.flags = IOPORT_F_INOUT; 551 iop.handler = fwctl_handler; 552 553 error = register_inout(&iop); 554 assert(error == 0); 555 556 bzero(&iop, sizeof(iop)); 557 iop.name = "fwctl_rreg"; 558 iop.port = FWCTL_IN; 559 iop.size = 1; 560 iop.flags = IOPORT_F_IN; 561 iop.handler = fwctl_handler; 562 563 error = register_inout(&iop); 564 assert(error == 0); 565 566 ops[OP_GET_LEN] = &fgetlen_info; 567 ops[OP_GET] = &fgetval_info; 568 569 be_state = IDENT_WAIT; 570 } 571