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