1/*- 2 * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: stable/10/usr.sbin/bhyve/fwctl.c 341607 2018-12-05 21:51:39Z emaste $ 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__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/fwctl.c 341607 2018-12-05 21:51:39Z emaste $"); 35 36#include <sys/param.h> 37#include <sys/types.h> 38#include <sys/errno.h> 39#include <sys/uio.h> 40 41#include <assert.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45 46#include "bhyverun.h" 47#include "inout.h" 48#include "fwctl.h" 49 50/* 51 * Messaging protocol base operations 52 */ 53#define OP_NULL 1 54#define OP_ECHO 2 55#define OP_GET 3 56#define OP_GET_LEN 4 57#define OP_SET 5 58#define OP_MAX OP_SET 59 60/* I/O ports */ 61#define FWCTL_OUT 0x510 62#define FWCTL_IN 0x511 63 64/* 65 * Back-end state-machine 66 */ 67enum state { 68 DORMANT, 69 IDENT_WAIT, 70 IDENT_SEND, 71 REQ, 72 RESP 73} be_state = DORMANT; 74 75static uint8_t sig[] = { 'B', 'H', 'Y', 'V' }; 76static u_int ident_idx; 77 78struct op_info { 79 int op; 80 int (*op_start)(uint32_t len); 81 void (*op_data)(uint32_t data, uint32_t len); 82 int (*op_result)(struct iovec **data); 83 void (*op_done)(struct iovec *data); 84}; 85static struct op_info *ops[OP_MAX+1]; 86 87/* Return 0-padded uint32_t */ 88static uint32_t 89fwctl_send_rest(uint32_t *data, size_t len) 90{ 91 union { 92 uint8_t c[4]; 93 uint32_t w; 94 } u; 95 uint8_t *cdata; 96 int i; 97 98 cdata = (uint8_t *) data; 99 u.w = 0; 100 101 for (i = 0, u.w = 0; i < len; i++) 102 u.c[i] = *cdata++; 103 104 return (u.w); 105} 106 107/* 108 * error op dummy proto - drop all data sent and return an error 109*/ 110static int errop_code; 111 112static void 113errop_set(int err) 114{ 115 116 errop_code = err; 117} 118 119static int 120errop_start(uint32_t len) 121{ 122 errop_code = ENOENT; 123 124 /* accept any length */ 125 return (errop_code); 126} 127 128static void 129errop_data(uint32_t data, uint32_t len) 130{ 131 132 /* ignore */ 133} 134 135static int 136errop_result(struct iovec **data) 137{ 138 139 /* no data to send back; always successful */ 140 *data = NULL; 141 return (errop_code); 142} 143 144static void 145errop_done(struct iovec *data) 146{ 147 148 /* assert data is NULL */ 149} 150 151static struct op_info errop_info = { 152 .op_start = errop_start, 153 .op_data = errop_data, 154 .op_result = errop_result, 155 .op_done = errop_done 156}; 157 158/* OID search */ 159SET_DECLARE(ctl_set, struct ctl); 160 161CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus)); 162 163static struct ctl * 164ctl_locate(const char *str, int maxlen) 165{ 166 struct ctl *cp, **cpp; 167 168 SET_FOREACH(cpp, ctl_set) { 169 cp = *cpp; 170 if (!strncmp(str, cp->c_oid, maxlen)) 171 return (cp); 172 } 173 174 return (NULL); 175} 176 177/* uefi-sysctl get-len */ 178#define FGET_STRSZ 80 179static struct iovec fget_biov[2]; 180static char fget_str[FGET_STRSZ]; 181static struct { 182 size_t f_sz; 183 uint32_t f_data[1024]; 184} fget_buf; 185static int fget_cnt; 186static size_t fget_size; 187 188static int 189fget_start(uint32_t len) 190{ 191 192 if (len > FGET_STRSZ) 193 return(E2BIG); 194 195 fget_cnt = 0; 196 197 return (0); 198} 199 200static void 201fget_data(uint32_t data, uint32_t len) 202{ 203 204 *((uint32_t *) &fget_str[fget_cnt]) = data; 205 fget_cnt += sizeof(uint32_t); 206} 207 208static int 209fget_result(struct iovec **data, int val) 210{ 211 struct ctl *cp; 212 int err; 213 214 err = 0; 215 216 /* Locate the OID */ 217 cp = ctl_locate(fget_str, fget_cnt); 218 if (cp == NULL) { 219 *data = NULL; 220 err = ENOENT; 221 } else { 222 if (val) { 223 /* For now, copy the len/data into a buffer */ 224 memset(&fget_buf, 0, sizeof(fget_buf)); 225 fget_buf.f_sz = cp->c_len; 226 memcpy(fget_buf.f_data, cp->c_data, cp->c_len); 227 fget_biov[0].iov_base = (char *)&fget_buf; 228 fget_biov[0].iov_len = sizeof(fget_buf.f_sz) + 229 cp->c_len; 230 } else { 231 fget_size = cp->c_len; 232 fget_biov[0].iov_base = (char *)&fget_size; 233 fget_biov[0].iov_len = sizeof(fget_size); 234 } 235 236 fget_biov[1].iov_base = NULL; 237 fget_biov[1].iov_len = 0; 238 *data = fget_biov; 239 } 240 241 return (err); 242} 243 244static void 245fget_done(struct iovec *data) 246{ 247 248 /* nothing needs to be freed */ 249} 250 251static int 252fget_len_result(struct iovec **data) 253{ 254 return (fget_result(data, 0)); 255} 256 257static int 258fget_val_result(struct iovec **data) 259{ 260 return (fget_result(data, 1)); 261} 262 263static struct op_info fgetlen_info = { 264 .op_start = fget_start, 265 .op_data = fget_data, 266 .op_result = fget_len_result, 267 .op_done = fget_done 268}; 269 270static struct op_info fgetval_info = { 271 .op_start = fget_start, 272 .op_data = fget_data, 273 .op_result = fget_val_result, 274 .op_done = fget_done 275}; 276 277static struct req_info { 278 int req_error; 279 u_int req_count; 280 uint32_t req_size; 281 uint32_t req_type; 282 uint32_t req_txid; 283 struct op_info *req_op; 284 int resp_error; 285 int resp_count; 286 size_t resp_size; 287 size_t resp_off; 288 struct iovec *resp_biov; 289} rinfo; 290 291static void 292fwctl_response_done(void) 293{ 294 295 (*rinfo.req_op->op_done)(rinfo.resp_biov); 296 297 /* reinit the req data struct */ 298 memset(&rinfo, 0, sizeof(rinfo)); 299} 300 301static void 302fwctl_request_done(void) 303{ 304 305 rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov); 306 307 /* XXX only a single vector supported at the moment */ 308 rinfo.resp_off = 0; 309 if (rinfo.resp_biov == NULL) { 310 rinfo.resp_size = 0; 311 } else { 312 rinfo.resp_size = rinfo.resp_biov[0].iov_len; 313 } 314} 315 316static int 317fwctl_request_start(void) 318{ 319 int err; 320 321 /* Data size doesn't include header */ 322 rinfo.req_size -= 12; 323 324 rinfo.req_op = &errop_info; 325 if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL) 326 rinfo.req_op = ops[rinfo.req_type]; 327 328 err = (*rinfo.req_op->op_start)(rinfo.req_size); 329 330 if (err) { 331 errop_set(err); 332 rinfo.req_op = &errop_info; 333 } 334 335 /* Catch case of zero-length message here */ 336 if (rinfo.req_size == 0) { 337 fwctl_request_done(); 338 return (1); 339 } 340 341 return (0); 342} 343 344static int 345fwctl_request_data(uint32_t value) 346{ 347 348 /* Make sure remaining size is >= 0 */ 349 if (rinfo.req_size <= sizeof(uint32_t)) 350 rinfo.req_size = 0; 351 else 352 rinfo.req_size -= sizeof(uint32_t); 353 354 (*rinfo.req_op->op_data)(value, rinfo.req_size); 355 356 if (rinfo.req_size < sizeof(uint32_t)) { 357 fwctl_request_done(); 358 return (1); 359 } 360 361 return (0); 362} 363 364static int 365fwctl_request(uint32_t value) 366{ 367 368 int ret; 369 370 ret = 0; 371 372 switch (rinfo.req_count) { 373 case 0: 374 /* Verify size */ 375 if (value < 12) { 376 printf("msg size error"); 377 exit(1); 378 } 379 rinfo.req_size = value; 380 rinfo.req_count = 1; 381 break; 382 case 1: 383 rinfo.req_type = value; 384 rinfo.req_count++; 385 break; 386 case 2: 387 rinfo.req_txid = value; 388 rinfo.req_count++; 389 ret = fwctl_request_start(); 390 break; 391 default: 392 ret = fwctl_request_data(value); 393 break; 394 } 395 396 return (ret); 397} 398 399static int 400fwctl_response(uint32_t *retval) 401{ 402 uint32_t *dp; 403 ssize_t remlen; 404 405 switch(rinfo.resp_count) { 406 case 0: 407 /* 4 x u32 header len + data */ 408 *retval = 4*sizeof(uint32_t) + 409 roundup(rinfo.resp_size, sizeof(uint32_t)); 410 rinfo.resp_count++; 411 break; 412 case 1: 413 *retval = rinfo.req_type; 414 rinfo.resp_count++; 415 break; 416 case 2: 417 *retval = rinfo.req_txid; 418 rinfo.resp_count++; 419 break; 420 case 3: 421 *retval = rinfo.resp_error; 422 rinfo.resp_count++; 423 break; 424 default: 425 remlen = rinfo.resp_size - rinfo.resp_off; 426 dp = (uint32_t *) 427 ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off); 428 if (remlen >= sizeof(uint32_t)) { 429 *retval = *dp; 430 } else if (remlen > 0) { 431 *retval = fwctl_send_rest(dp, remlen); 432 } 433 rinfo.resp_off += sizeof(uint32_t); 434 break; 435 } 436 437 if (rinfo.resp_count > 3 && 438 rinfo.resp_off >= rinfo.resp_size) { 439 fwctl_response_done(); 440 return (1); 441 } 442 443 return (0); 444} 445 446 447/* 448 * i/o port handling. 449 */ 450static uint8_t 451fwctl_inb(void) 452{ 453 uint8_t retval; 454 455 retval = 0xff; 456 457 switch (be_state) { 458 case IDENT_SEND: 459 retval = sig[ident_idx++]; 460 if (ident_idx >= sizeof(sig)) 461 be_state = REQ; 462 break; 463 default: 464 break; 465 } 466 467 return (retval); 468} 469 470static void 471fwctl_outw(uint16_t val) 472{ 473 switch (be_state) { 474 case IDENT_WAIT: 475 if (val == 0) { 476 be_state = IDENT_SEND; 477 ident_idx = 0; 478 } 479 break; 480 default: 481 /* ignore */ 482 break; 483 } 484} 485 486static uint32_t 487fwctl_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 504static void 505fwctl_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 518static int 519fwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 520 uint32_t *eax, void *arg) 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} 539INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler); 540INOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler); 541 542void 543fwctl_init(void) 544{ 545 546 ops[OP_GET_LEN] = &fgetlen_info; 547 ops[OP_GET] = &fgetval_info; 548 549 be_state = IDENT_WAIT; 550} 551