mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-29 12:35:53 +01:00
433 lines
8.1 KiB
C
433 lines
8.1 KiB
C
|
/** @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;
|
||
|
}
|