1288522Sgrehan/*- 2288522Sgrehan * Copyright (c) 2015 Peter Grehan <grehan@freebsd.org> 3288522Sgrehan * All rights reserved. 4288522Sgrehan * 5288522Sgrehan * Redistribution and use in source and binary forms, with or without 6288522Sgrehan * modification, are permitted provided that the following conditions 7288522Sgrehan * are met: 8288522Sgrehan * 1. Redistributions of source code must retain the above copyright 9288522Sgrehan * notice, this list of conditions and the following disclaimer. 10288522Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11288522Sgrehan * notice, this list of conditions and the following disclaimer in the 12288522Sgrehan * documentation and/or other materials provided with the distribution. 13288522Sgrehan * 14288522Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 15288522Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16288522Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17288522Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18288522Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19288522Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20288522Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21288522Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22288522Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23288522Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24288522Sgrehan * SUCH DAMAGE. 25288522Sgrehan * 26288522Sgrehan * $FreeBSD: releng/11.0/usr.sbin/bhyve/fwctl.c 298454 2016-04-22 06:25:32Z araujo $ 27288522Sgrehan */ 28288522Sgrehan 29288522Sgrehan/* 30288522Sgrehan * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does, 31288522Sgrehan * but with a request/response messaging protocol. 32288522Sgrehan */ 33288522Sgrehan#include <sys/cdefs.h> 34288522Sgrehan__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/fwctl.c 298454 2016-04-22 06:25:32Z araujo $"); 35288522Sgrehan 36288522Sgrehan#include <sys/param.h> 37288522Sgrehan#include <sys/types.h> 38288522Sgrehan#include <sys/errno.h> 39288522Sgrehan#include <sys/uio.h> 40288522Sgrehan 41288522Sgrehan#include <assert.h> 42288522Sgrehan#include <stdio.h> 43288522Sgrehan#include <stdlib.h> 44288522Sgrehan#include <string.h> 45288522Sgrehan 46288522Sgrehan#include "bhyverun.h" 47288522Sgrehan#include "inout.h" 48288522Sgrehan#include "fwctl.h" 49288522Sgrehan 50288522Sgrehan/* 51288522Sgrehan * Messaging protocol base operations 52288522Sgrehan */ 53288522Sgrehan#define OP_NULL 1 54288522Sgrehan#define OP_ECHO 2 55288522Sgrehan#define OP_GET 3 56288522Sgrehan#define OP_GET_LEN 4 57288522Sgrehan#define OP_SET 5 58288522Sgrehan#define OP_MAX OP_SET 59288522Sgrehan 60288522Sgrehan/* I/O ports */ 61288522Sgrehan#define FWCTL_OUT 0x510 62288522Sgrehan#define FWCTL_IN 0x511 63288522Sgrehan 64288522Sgrehan/* 65288522Sgrehan * Back-end state-machine 66288522Sgrehan */ 67288522Sgrehanenum state { 68288522Sgrehan DORMANT, 69288522Sgrehan IDENT_WAIT, 70288522Sgrehan IDENT_SEND, 71288522Sgrehan REQ, 72288522Sgrehan RESP 73288522Sgrehan} be_state = DORMANT; 74288522Sgrehan 75288522Sgrehanstatic uint8_t sig[] = { 'B', 'H', 'Y', 'V' }; 76288522Sgrehanstatic u_int ident_idx; 77288522Sgrehan 78288522Sgrehanstruct op_info { 79288522Sgrehan int op; 80288522Sgrehan int (*op_start)(int len); 81288522Sgrehan void (*op_data)(uint32_t data, int len); 82288522Sgrehan int (*op_result)(struct iovec **data); 83288522Sgrehan void (*op_done)(struct iovec *data); 84288522Sgrehan}; 85288522Sgrehanstatic struct op_info *ops[OP_MAX+1]; 86288522Sgrehan 87288522Sgrehan/* Return 0-padded uint32_t */ 88288522Sgrehanstatic uint32_t 89288522Sgrehanfwctl_send_rest(uint32_t *data, size_t len) 90288522Sgrehan{ 91288522Sgrehan union { 92288522Sgrehan uint8_t c[4]; 93288522Sgrehan uint32_t w; 94288522Sgrehan } u; 95288522Sgrehan uint8_t *cdata; 96288522Sgrehan int i; 97288522Sgrehan 98288522Sgrehan cdata = (uint8_t *) data; 99288522Sgrehan u.w = 0; 100288522Sgrehan 101288522Sgrehan for (i = 0, u.w = 0; i < len; i++) 102288522Sgrehan u.c[i] = *cdata++; 103288522Sgrehan 104288522Sgrehan return (u.w); 105288522Sgrehan} 106288522Sgrehan 107288522Sgrehan/* 108288522Sgrehan * error op dummy proto - drop all data sent and return an error 109288522Sgrehan*/ 110288522Sgrehanstatic int errop_code; 111288522Sgrehan 112288522Sgrehanstatic void 113288522Sgrehanerrop_set(int err) 114288522Sgrehan{ 115288522Sgrehan 116288522Sgrehan errop_code = err; 117288522Sgrehan} 118288522Sgrehan 119288522Sgrehanstatic int 120288522Sgrehanerrop_start(int len) 121288522Sgrehan{ 122288522Sgrehan errop_code = ENOENT; 123288522Sgrehan 124288522Sgrehan /* accept any length */ 125288522Sgrehan return (errop_code); 126288522Sgrehan} 127288522Sgrehan 128288522Sgrehanstatic void 129288522Sgrehanerrop_data(uint32_t data, int len) 130288522Sgrehan{ 131288522Sgrehan 132288522Sgrehan /* ignore */ 133288522Sgrehan} 134288522Sgrehan 135288522Sgrehanstatic int 136288522Sgrehanerrop_result(struct iovec **data) 137288522Sgrehan{ 138288522Sgrehan 139288522Sgrehan /* no data to send back; always successful */ 140288522Sgrehan *data = NULL; 141288522Sgrehan return (errop_code); 142288522Sgrehan} 143288522Sgrehan 144288522Sgrehanstatic void 145288522Sgrehanerrop_done(struct iovec *data) 146288522Sgrehan{ 147288522Sgrehan 148288522Sgrehan /* assert data is NULL */ 149288522Sgrehan} 150288522Sgrehan 151288522Sgrehanstatic struct op_info errop_info = { 152288522Sgrehan .op_start = errop_start, 153288522Sgrehan .op_data = errop_data, 154288522Sgrehan .op_result = errop_result, 155288522Sgrehan .op_done = errop_done 156288522Sgrehan}; 157288522Sgrehan 158288522Sgrehan/* OID search */ 159288522SgrehanSET_DECLARE(ctl_set, struct ctl); 160288522Sgrehan 161288522SgrehanCTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus)); 162288522Sgrehan 163288522Sgrehanstatic struct ctl * 164288522Sgrehanctl_locate(const char *str, int maxlen) 165288522Sgrehan{ 166288522Sgrehan struct ctl *cp, **cpp; 167288522Sgrehan 168288522Sgrehan SET_FOREACH(cpp, ctl_set) { 169288522Sgrehan cp = *cpp; 170288522Sgrehan if (!strncmp(str, cp->c_oid, maxlen)) 171288522Sgrehan return (cp); 172288522Sgrehan } 173288522Sgrehan 174288522Sgrehan return (NULL); 175288522Sgrehan} 176288522Sgrehan 177288522Sgrehan/* uefi-sysctl get-len */ 178288522Sgrehan#define FGET_STRSZ 80 179288522Sgrehanstatic struct iovec fget_biov[2]; 180288522Sgrehanstatic char fget_str[FGET_STRSZ]; 181288522Sgrehanstatic struct { 182288522Sgrehan size_t f_sz; 183288522Sgrehan uint32_t f_data[1024]; 184288522Sgrehan} fget_buf; 185288522Sgrehanstatic int fget_cnt; 186288522Sgrehanstatic size_t fget_size; 187288522Sgrehan 188288522Sgrehanstatic int 189288522Sgrehanfget_start(int len) 190288522Sgrehan{ 191288522Sgrehan 192288522Sgrehan if (len > FGET_STRSZ) 193288522Sgrehan return(E2BIG); 194288522Sgrehan 195288522Sgrehan fget_cnt = 0; 196288522Sgrehan 197288522Sgrehan return (0); 198288522Sgrehan} 199288522Sgrehan 200288522Sgrehanstatic void 201288522Sgrehanfget_data(uint32_t data, int len) 202288522Sgrehan{ 203288522Sgrehan 204288522Sgrehan *((uint32_t *) &fget_str[fget_cnt]) = data; 205288522Sgrehan fget_cnt += sizeof(uint32_t); 206288522Sgrehan} 207288522Sgrehan 208288522Sgrehanstatic int 209288522Sgrehanfget_result(struct iovec **data, int val) 210288522Sgrehan{ 211288522Sgrehan struct ctl *cp; 212288522Sgrehan int err; 213288522Sgrehan 214288522Sgrehan err = 0; 215288522Sgrehan 216288522Sgrehan /* Locate the OID */ 217288522Sgrehan cp = ctl_locate(fget_str, fget_cnt); 218288522Sgrehan if (cp == NULL) { 219288522Sgrehan *data = NULL; 220288522Sgrehan err = ENOENT; 221288522Sgrehan } else { 222288522Sgrehan if (val) { 223288522Sgrehan /* For now, copy the len/data into a buffer */ 224288522Sgrehan memset(&fget_buf, 0, sizeof(fget_buf)); 225288522Sgrehan fget_buf.f_sz = cp->c_len; 226288522Sgrehan memcpy(fget_buf.f_data, cp->c_data, cp->c_len); 227288522Sgrehan fget_biov[0].iov_base = (char *)&fget_buf; 228288522Sgrehan fget_biov[0].iov_len = sizeof(fget_buf.f_sz) + 229288522Sgrehan cp->c_len; 230288522Sgrehan } else { 231288522Sgrehan fget_size = cp->c_len; 232288522Sgrehan fget_biov[0].iov_base = (char *)&fget_size; 233288522Sgrehan fget_biov[0].iov_len = sizeof(fget_size); 234288522Sgrehan } 235288522Sgrehan 236288522Sgrehan fget_biov[1].iov_base = NULL; 237288522Sgrehan fget_biov[1].iov_len = 0; 238288522Sgrehan *data = fget_biov; 239288522Sgrehan } 240288522Sgrehan 241288522Sgrehan return (err); 242288522Sgrehan} 243288522Sgrehan 244288522Sgrehanstatic void 245288522Sgrehanfget_done(struct iovec *data) 246288522Sgrehan{ 247288522Sgrehan 248288522Sgrehan /* nothing needs to be freed */ 249288522Sgrehan} 250288522Sgrehan 251288522Sgrehanstatic int 252288522Sgrehanfget_len_result(struct iovec **data) 253288522Sgrehan{ 254288522Sgrehan return (fget_result(data, 0)); 255288522Sgrehan} 256288522Sgrehan 257288522Sgrehanstatic int 258288522Sgrehanfget_val_result(struct iovec **data) 259288522Sgrehan{ 260288522Sgrehan return (fget_result(data, 1)); 261288522Sgrehan} 262288522Sgrehan 263288522Sgrehanstatic struct op_info fgetlen_info = { 264288522Sgrehan .op_start = fget_start, 265288522Sgrehan .op_data = fget_data, 266288522Sgrehan .op_result = fget_len_result, 267288522Sgrehan .op_done = fget_done 268288522Sgrehan}; 269288522Sgrehan 270288522Sgrehanstatic struct op_info fgetval_info = { 271288522Sgrehan .op_start = fget_start, 272288522Sgrehan .op_data = fget_data, 273288522Sgrehan .op_result = fget_val_result, 274288522Sgrehan .op_done = fget_done 275288522Sgrehan}; 276288522Sgrehan 277288522Sgrehanstatic struct req_info { 278288522Sgrehan int req_error; 279288522Sgrehan u_int req_count; 280288522Sgrehan uint32_t req_size; 281288522Sgrehan uint32_t req_type; 282288522Sgrehan uint32_t req_txid; 283288522Sgrehan struct op_info *req_op; 284288522Sgrehan int resp_error; 285288522Sgrehan int resp_count; 286288522Sgrehan int resp_size; 287288522Sgrehan int resp_off; 288288522Sgrehan struct iovec *resp_biov; 289288522Sgrehan} rinfo; 290288522Sgrehan 291288522Sgrehanstatic void 292288522Sgrehanfwctl_response_done(void) 293288522Sgrehan{ 294288522Sgrehan 295288522Sgrehan (*rinfo.req_op->op_done)(rinfo.resp_biov); 296288522Sgrehan 297288522Sgrehan /* reinit the req data struct */ 298288522Sgrehan memset(&rinfo, 0, sizeof(rinfo)); 299288522Sgrehan} 300288522Sgrehan 301288522Sgrehanstatic void 302288522Sgrehanfwctl_request_done(void) 303288522Sgrehan{ 304288522Sgrehan 305288522Sgrehan rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov); 306288522Sgrehan 307288522Sgrehan /* XXX only a single vector supported at the moment */ 308288522Sgrehan rinfo.resp_off = 0; 309288522Sgrehan if (rinfo.resp_biov == NULL) { 310288522Sgrehan rinfo.resp_size = 0; 311288522Sgrehan } else { 312288522Sgrehan rinfo.resp_size = rinfo.resp_biov[0].iov_len; 313288522Sgrehan } 314288522Sgrehan} 315288522Sgrehan 316288522Sgrehanstatic int 317288522Sgrehanfwctl_request_start(void) 318288522Sgrehan{ 319288522Sgrehan int err; 320288522Sgrehan 321288522Sgrehan /* Data size doesn't include header */ 322288522Sgrehan rinfo.req_size -= 12; 323288522Sgrehan 324288522Sgrehan rinfo.req_op = &errop_info; 325288522Sgrehan if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL) 326288522Sgrehan rinfo.req_op = ops[rinfo.req_type]; 327288522Sgrehan 328288522Sgrehan err = (*rinfo.req_op->op_start)(rinfo.req_size); 329288522Sgrehan 330288522Sgrehan if (err) { 331288522Sgrehan errop_set(err); 332288522Sgrehan rinfo.req_op = &errop_info; 333288522Sgrehan } 334288522Sgrehan 335288522Sgrehan /* Catch case of zero-length message here */ 336288522Sgrehan if (rinfo.req_size == 0) { 337288522Sgrehan fwctl_request_done(); 338288522Sgrehan return (1); 339288522Sgrehan } 340288522Sgrehan 341288522Sgrehan return (0); 342288522Sgrehan} 343288522Sgrehan 344288522Sgrehanstatic int 345288522Sgrehanfwctl_request_data(uint32_t value) 346288522Sgrehan{ 347288522Sgrehan int remlen; 348288522Sgrehan 349288522Sgrehan /* Make sure remaining size is >= 0 */ 350288522Sgrehan rinfo.req_size -= sizeof(uint32_t); 351298454Saraujo remlen = MAX(rinfo.req_size, 0); 352288522Sgrehan 353288522Sgrehan (*rinfo.req_op->op_data)(value, remlen); 354288522Sgrehan 355288522Sgrehan if (rinfo.req_size < sizeof(uint32_t)) { 356288522Sgrehan fwctl_request_done(); 357288522Sgrehan return (1); 358288522Sgrehan } 359288522Sgrehan 360288522Sgrehan return (0); 361288522Sgrehan} 362288522Sgrehan 363288522Sgrehanstatic int 364288522Sgrehanfwctl_request(uint32_t value) 365288522Sgrehan{ 366288522Sgrehan 367288522Sgrehan int ret; 368288522Sgrehan 369288522Sgrehan ret = 0; 370288522Sgrehan 371288522Sgrehan switch (rinfo.req_count) { 372288522Sgrehan case 0: 373288522Sgrehan /* Verify size */ 374288522Sgrehan if (value < 12) { 375288522Sgrehan printf("msg size error"); 376288522Sgrehan exit(1); 377288522Sgrehan } 378288522Sgrehan rinfo.req_size = value; 379288522Sgrehan rinfo.req_count = 1; 380288522Sgrehan break; 381288522Sgrehan case 1: 382288522Sgrehan rinfo.req_type = value; 383288522Sgrehan rinfo.req_count++; 384288522Sgrehan break; 385288522Sgrehan case 2: 386288522Sgrehan rinfo.req_txid = value; 387288522Sgrehan rinfo.req_count++; 388288522Sgrehan ret = fwctl_request_start(); 389288522Sgrehan break; 390288522Sgrehan default: 391288522Sgrehan ret = fwctl_request_data(value); 392288522Sgrehan break; 393288522Sgrehan } 394288522Sgrehan 395288522Sgrehan return (ret); 396288522Sgrehan} 397288522Sgrehan 398288522Sgrehanstatic int 399288522Sgrehanfwctl_response(uint32_t *retval) 400288522Sgrehan{ 401288522Sgrehan uint32_t *dp; 402288522Sgrehan int remlen; 403288522Sgrehan 404288522Sgrehan switch(rinfo.resp_count) { 405288522Sgrehan case 0: 406288522Sgrehan /* 4 x u32 header len + data */ 407288522Sgrehan *retval = 4*sizeof(uint32_t) + 408288522Sgrehan roundup(rinfo.resp_size, sizeof(uint32_t)); 409288522Sgrehan rinfo.resp_count++; 410288522Sgrehan break; 411288522Sgrehan case 1: 412288522Sgrehan *retval = rinfo.req_type; 413288522Sgrehan rinfo.resp_count++; 414288522Sgrehan break; 415288522Sgrehan case 2: 416288522Sgrehan *retval = rinfo.req_txid; 417288522Sgrehan rinfo.resp_count++; 418288522Sgrehan break; 419288522Sgrehan case 3: 420288522Sgrehan *retval = rinfo.resp_error; 421288522Sgrehan rinfo.resp_count++; 422288522Sgrehan break; 423288522Sgrehan default: 424288522Sgrehan remlen = rinfo.resp_size - rinfo.resp_off; 425288522Sgrehan dp = (uint32_t *) 426288522Sgrehan ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off); 427288522Sgrehan if (remlen >= sizeof(uint32_t)) { 428288522Sgrehan *retval = *dp; 429288522Sgrehan } else if (remlen > 0) { 430288522Sgrehan *retval = fwctl_send_rest(dp, remlen); 431288522Sgrehan } 432288522Sgrehan rinfo.resp_off += sizeof(uint32_t); 433288522Sgrehan break; 434288522Sgrehan } 435288522Sgrehan 436288522Sgrehan if (rinfo.resp_count > 3 && 437288522Sgrehan rinfo.resp_size - rinfo.resp_off <= 0) { 438288522Sgrehan fwctl_response_done(); 439288522Sgrehan return (1); 440288522Sgrehan } 441288522Sgrehan 442288522Sgrehan return (0); 443288522Sgrehan} 444288522Sgrehan 445288522Sgrehan 446288522Sgrehan/* 447288522Sgrehan * i/o port handling. 448288522Sgrehan */ 449288522Sgrehanstatic uint8_t 450288522Sgrehanfwctl_inb(void) 451288522Sgrehan{ 452288522Sgrehan uint8_t retval; 453288522Sgrehan 454288522Sgrehan retval = 0xff; 455288522Sgrehan 456288522Sgrehan switch (be_state) { 457288522Sgrehan case IDENT_SEND: 458288522Sgrehan retval = sig[ident_idx++]; 459288522Sgrehan if (ident_idx >= sizeof(sig)) 460288522Sgrehan be_state = REQ; 461288522Sgrehan break; 462288522Sgrehan default: 463288522Sgrehan break; 464288522Sgrehan } 465288522Sgrehan 466288522Sgrehan return (retval); 467288522Sgrehan} 468288522Sgrehan 469288522Sgrehanstatic void 470288522Sgrehanfwctl_outw(uint16_t val) 471288522Sgrehan{ 472288522Sgrehan switch (be_state) { 473288522Sgrehan case IDENT_WAIT: 474288522Sgrehan if (val == 0) { 475288522Sgrehan be_state = IDENT_SEND; 476288522Sgrehan ident_idx = 0; 477288522Sgrehan } 478288522Sgrehan break; 479288522Sgrehan default: 480288522Sgrehan /* ignore */ 481288522Sgrehan break; 482288522Sgrehan } 483288522Sgrehan} 484288522Sgrehan 485288522Sgrehanstatic uint32_t 486288522Sgrehanfwctl_inl(void) 487288522Sgrehan{ 488288522Sgrehan uint32_t retval; 489288522Sgrehan 490288522Sgrehan switch (be_state) { 491288522Sgrehan case RESP: 492288522Sgrehan if (fwctl_response(&retval)) 493288522Sgrehan be_state = REQ; 494288522Sgrehan break; 495288522Sgrehan default: 496288522Sgrehan retval = 0xffffffff; 497288522Sgrehan break; 498288522Sgrehan } 499288522Sgrehan 500288522Sgrehan return (retval); 501288522Sgrehan} 502288522Sgrehan 503288522Sgrehanstatic void 504288522Sgrehanfwctl_outl(uint32_t val) 505288522Sgrehan{ 506288522Sgrehan 507288522Sgrehan switch (be_state) { 508288522Sgrehan case REQ: 509288522Sgrehan if (fwctl_request(val)) 510288522Sgrehan be_state = RESP; 511288522Sgrehan default: 512288522Sgrehan break; 513288522Sgrehan } 514288522Sgrehan 515288522Sgrehan} 516288522Sgrehan 517288522Sgrehanstatic int 518288522Sgrehanfwctl_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 519288522Sgrehan uint32_t *eax, void *arg) 520288522Sgrehan{ 521288522Sgrehan 522288522Sgrehan if (in) { 523288522Sgrehan if (bytes == 1) 524288522Sgrehan *eax = fwctl_inb(); 525288522Sgrehan else if (bytes == 4) 526288522Sgrehan *eax = fwctl_inl(); 527288522Sgrehan else 528288522Sgrehan *eax = 0xffff; 529288522Sgrehan } else { 530288522Sgrehan if (bytes == 2) 531288522Sgrehan fwctl_outw(*eax); 532288522Sgrehan else if (bytes == 4) 533288522Sgrehan fwctl_outl(*eax); 534288522Sgrehan } 535288522Sgrehan 536288522Sgrehan return (0); 537288522Sgrehan} 538288522SgrehanINOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler); 539288524SgrehanINOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler); 540288522Sgrehan 541288522Sgrehanvoid 542288522Sgrehanfwctl_init(void) 543288522Sgrehan{ 544288522Sgrehan 545288522Sgrehan ops[OP_GET_LEN] = &fgetlen_info; 546288522Sgrehan ops[OP_GET] = &fgetval_info; 547288522Sgrehan 548288522Sgrehan be_state = IDENT_WAIT; 549288522Sgrehan} 550