| /** @file | |
| Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com> | |
| Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR> | |
| Copyright (C) 2013, Red Hat, Inc. | |
| Copyright (c) 2015, Nahanni Systems. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "Uefi.h" | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/BhyveFwCtlLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/IoLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #define FW_PORT 0x510 | |
| #define FW_IPORT 0x511 | |
| /* Transport protocol basic operations */ | |
| #define OP_NULL 1 | |
| #define OP_ECHO 2 | |
| #define OP_GET 3 | |
| #define OP_GET_LEN 4 | |
| #define OP_SET 5 | |
| /* Transport protocol error returns */ | |
| #define T_ESUCCESS 0 | |
| #define T_ENOENT 2 | |
| #define T_E2BIG 7 | |
| #define T_EMSGSIZE 40 | |
| #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) | |
| STATIC CONST CHAR8 mBhyveSig[4] = { 'B', 'H', 'Y', 'V' }; | |
| STATIC BOOLEAN mBhyveFwCtlSupported = FALSE; | |
| STATIC INT32 mBhyveFwCtlTxid = 0xa5; | |
| /* XXX Maybe a better inbuilt version of this ? */ | |
| typedef struct { | |
| VOID *Base; | |
| UINT32 Len; | |
| } BIO_VEC; | |
| typedef struct { | |
| UINT32 Sz; | |
| UINT32 Op; | |
| UINT32 TxId; | |
| UINT32 Err; | |
| } MSG_RX_HDR; | |
| STATIC | |
| RETURN_STATUS | |
| EFIAPI | |
| BhyveFwCtl_CvtErr ( | |
| IN UINT32 errno | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| switch (errno) { | |
| case T_ESUCCESS: | |
| Status = RETURN_SUCCESS; | |
| break; | |
| case T_ENOENT: | |
| Status = RETURN_NOT_FOUND; | |
| break; | |
| case T_E2BIG: | |
| Status = RETURN_INVALID_PARAMETER; | |
| break; | |
| case T_EMSGSIZE: | |
| Status = RETURN_BUFFER_TOO_SMALL; | |
| break; | |
| default: | |
| Status = RETURN_PROTOCOL_ERROR; | |
| break; | |
| } | |
| return Status; | |
| } | |
| STATIC | |
| UINT32 | |
| EFIAPI | |
| BIov_WLen ( | |
| IN BIO_VEC b[] | |
| ) | |
| { | |
| UINT32 i; | |
| UINT32 tLen; | |
| tLen = 0; | |
| if (b != NULL) { | |
| for (i = 0; b[i].Base != NULL; i++) { | |
| tLen += ROUNDUP (b[i].Len, sizeof (UINT32)); | |
| } | |
| } | |
| return tLen; | |
| } | |
| /** | |
| Utility to send 1-3 bytes of input as a 4-byte value | |
| with trailing zeroes. | |
| **/ | |
| STATIC | |
| UINT32 | |
| BIov_Send_Rem ( | |
| IN UINT32 *Data, | |
| IN UINT32 Len | |
| ) | |
| { | |
| union { | |
| UINT8 c[4]; | |
| UINT32 w; | |
| } u; | |
| UINT8 *cdata; | |
| UINT32 i; | |
| cdata = (UINT8 *)Data; | |
| u.w = 0; | |
| for (i = 0; i < Len; i++) { | |
| u.c[i] = *cdata++; | |
| } | |
| return u.w; | |
| } | |
| /** | |
| Send a block of data out the i/o port as 4-byte quantities, | |
| appending trailing zeroes on the last if required. | |
| **/ | |
| STATIC | |
| VOID | |
| BIov_Send ( | |
| IN char *Data, | |
| IN UINT32 Len | |
| ) | |
| { | |
| UINT32 *LData; | |
| LData = (UINT32 *)Data; | |
| while (Len > sizeof (UINT32)) { | |
| IoWrite32 (FW_PORT, *LData++); | |
| Len -= sizeof (UINT32); | |
| } | |
| if (Len > 0) { | |
| IoWrite32 (FW_PORT, BIov_Send_Rem (LData, Len)); | |
| } | |
| } | |
| /** | |
| Send data described by an array of iovecs out the i/o port. | |
| **/ | |
| STATIC | |
| VOID | |
| BIov_SendAll ( | |
| IN BIO_VEC b[] | |
| ) | |
| { | |
| INT32 i; | |
| if (b != NULL) { | |
| for (i = 0; b[i].Base; i++) { | |
| BIov_Send (b[i].Base, b[i].Len); | |
| } | |
| } | |
| } | |
| /** | |
| Prepend the transport header to a block of data and send. | |
| **/ | |
| STATIC | |
| VOID | |
| EFIAPI | |
| BhyveFwCtl_MsgSend ( | |
| IN UINT32 OpCode, | |
| IN BIO_VEC Data[] | |
| ) | |
| { | |
| BIO_VEC hIov[4]; | |
| UINT32 Hdr[3]; | |
| UINT32 i; | |
| /* Set up header as an iovec */ | |
| for (i = 0; i < 3; i++) { | |
| hIov[i].Base = &Hdr[i]; | |
| hIov[i].Len = sizeof (Hdr[0]); | |
| } | |
| hIov[i].Base = NULL; | |
| hIov[i].Len = 0; | |
| /* Initialize header */ | |
| Hdr[0] = BIov_WLen (hIov) + BIov_WLen (Data); | |
| Hdr[1] = (UINT32)OpCode; | |
| Hdr[2] = mBhyveFwCtlTxid; | |
| /* Send header and data */ | |
| BIov_SendAll (hIov); | |
| BIov_SendAll (Data); | |
| } | |
| /** | |
| Read a transport response and optional data from the i/o port. | |
| **/ | |
| STATIC | |
| RETURN_STATUS | |
| EFIAPI | |
| BhyveFwCtl_MsgRecv ( | |
| OUT MSG_RX_HDR *Rhdr, | |
| OUT BIO_VEC Data[] | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| UINT32 *Dp; | |
| UINT32 Rd; | |
| UINT32 remLen; | |
| INT32 oLen; | |
| INT32 xLen; | |
| Rd = IoRead32 (FW_PORT); | |
| if (Rd < sizeof (MSG_RX_HDR)) { | |
| } | |
| /* Read in header and setup initial error */ | |
| Rhdr->Sz = Rd; | |
| Rhdr->Op = IoRead32 (FW_PORT); | |
| Rhdr->TxId = IoRead32 (FW_PORT); | |
| Rhdr->Err = IoRead32 (FW_PORT); | |
| /* Convert transport errno into UEFI error status */ | |
| Status = BhyveFwCtl_CvtErr (Rhdr->Err); | |
| remLen = Rd - sizeof (MSG_RX_HDR); | |
| xLen = 0; | |
| /* | |
| * A few cases to handle: | |
| * - the user didn't supply a read buffer | |
| * - the buffer is too small for the response | |
| * - the response is zero-length | |
| */ | |
| if (Data != NULL) { | |
| Dp = (UINT32 *)Data[0].Base; | |
| oLen = remLen; | |
| if (remLen > Data[0].Len) { | |
| Status = RETURN_BUFFER_TOO_SMALL; | |
| xLen = remLen - Data[0].Len; | |
| oLen = remLen = Data[0].Len; | |
| } | |
| while (remLen > 0) { | |
| *Dp++ = IoRead32 (FW_PORT); | |
| remLen -= sizeof (UINT32); | |
| } | |
| Data[0].Len = oLen; | |
| } else { | |
| /* No user data, but data returned - drop */ | |
| if (remLen > 0) { | |
| Status = RETURN_BUFFER_TOO_SMALL; | |
| xLen = remLen; | |
| } | |
| } | |
| /* Drop additional data */ | |
| while (xLen > 0) { | |
| (void)IoRead32 (FW_PORT); | |
| xLen -= sizeof (UINT32); | |
| } | |
| return Status; | |
| } | |
| STATIC | |
| RETURN_STATUS | |
| EFIAPI | |
| BhyveFwCtl_Msg ( | |
| IN UINT32 OpCode, | |
| IN BIO_VEC Sdata[], | |
| OUT BIO_VEC Rdata[] | |
| ) | |
| { | |
| MSG_RX_HDR Rh; | |
| RETURN_STATUS Status; | |
| Status = RETURN_SUCCESS; | |
| BhyveFwCtl_MsgSend (OpCode, Sdata); | |
| Status = BhyveFwCtl_MsgRecv (&Rh, Rdata); | |
| mBhyveFwCtlTxid++; | |
| return Status; | |
| } | |
| STATIC | |
| RETURN_STATUS | |
| EFIAPI | |
| BhyveFwCtlGetLen ( | |
| IN CONST CHAR8 *Name, | |
| IN OUT UINT32 *Size | |
| ) | |
| { | |
| BIO_VEC Req[2], Resp[2]; | |
| RETURN_STATUS Status; | |
| Req[0].Base = (VOID *)Name; | |
| Req[0].Len = (UINT32)AsciiStrLen (Name) + 1; | |
| Req[1].Base = NULL; | |
| Resp[0].Base = Size; | |
| Resp[0].Len = sizeof (UINT32); | |
| Resp[1].Base = NULL; | |
| Status = BhyveFwCtl_Msg (OP_GET_LEN, Req, Resp); | |
| return Status; | |
| } | |
| #define FMAXSZ 1024 | |
| STATIC struct { | |
| UINT64 fSize; | |
| UINT32 fData[FMAXSZ]; | |
| } FwGetvalBuf; | |
| STATIC | |
| RETURN_STATUS | |
| EFIAPI | |
| BhyveFwCtlGetVal ( | |
| IN CONST CHAR8 *Name, | |
| OUT VOID *Item, | |
| IN OUT UINT32 *Size | |
| ) | |
| { | |
| BIO_VEC Req[2]; | |
| BIO_VEC Resp[2]; | |
| RETURN_STATUS Status; | |
| /* Make sure temp buffer is larger than passed-in size */ | |
| if (*Size > sizeof (FwGetvalBuf.fData)) { | |
| return RETURN_INVALID_PARAMETER; | |
| } | |
| Req[0].Base = (VOID *)Name; | |
| Req[0].Len = (UINT32)AsciiStrLen (Name) + 1; | |
| Req[1].Base = NULL; | |
| Resp[0].Base = &FwGetvalBuf; | |
| Resp[0].Len = sizeof (UINT64) + *Size; | |
| Resp[1].Base = NULL; | |
| Status = BhyveFwCtl_Msg (OP_GET, Req, Resp); | |
| /* | |
| * Copy out data on success (or on a truncated message). | |
| * XXX This step can be eliminted with Msg() supporting | |
| * multiple iovecs. | |
| */ | |
| if ((Status == RETURN_SUCCESS) || (Status == RETURN_BUFFER_TOO_SMALL)) { | |
| *Size = (UINT32)FwGetvalBuf.fSize; | |
| CopyMem (Item, FwGetvalBuf.fData, *Size); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Front end to the internal GET_LEN and GET protocols | |
| **/ | |
| RETURN_STATUS | |
| EFIAPI | |
| BhyveFwCtlGet ( | |
| IN CONST CHAR8 *Name, | |
| OUT VOID *Item, | |
| IN OUT UINTN *Size | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| if (mBhyveFwCtlSupported == FALSE) { | |
| return RETURN_UNSUPPORTED; | |
| } | |
| if (Item == NULL) { | |
| Status = BhyveFwCtlGetLen (Name, (UINT32 *)Size); | |
| } else { | |
| Status = BhyveFwCtlGetVal (Name, Item, (UINT32 *)Size); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Library initialization. Probe the host to see if the f/w ctl | |
| interface is supported. | |
| **/ | |
| RETURN_STATUS | |
| EFIAPI | |
| BhyveFwCtlInitialize ( | |
| VOID | |
| ) | |
| { | |
| UINT32 i; | |
| UINT8 ch; | |
| DEBUG ((DEBUG_INFO, "FwCtlInitialize\n")); | |
| IoWrite16 (FW_PORT, 0x0000); | |
| for (i = 0; i < 4; i++) { | |
| ch = IoRead8 (FW_IPORT); | |
| if (ch != mBhyveSig[i]) { | |
| DEBUG ((DEBUG_INFO, "Host f/w sig mismatch %c/%c\n", ch, mBhyveSig[i])); | |
| return RETURN_SUCCESS; | |
| } | |
| } | |
| mBhyveFwCtlSupported = TRUE; | |
| return RETURN_SUCCESS; | |
| } |