1 /** @file
2 
3   Copyright (c) 2020, Rebecca Cran <rebecca@bsdio.com>
4   Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
5   Copyright (C) 2013, Red Hat, Inc.
6   Copyright (c) 2015, Nahanni Systems.
7 
8   SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include "Uefi.h"
13 #include <Library/BaseLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/DebugLib.h>
16 #include <Library/IoLib.h>
17 #include <Library/BhyveFwCtlLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 
21 #define FW_PORT        0x510
22 #define FW_IPORT    0x511
23 
24 /* Transport protocol basic operations */
25 #define OP_NULL        1
26 #define OP_ECHO        2
27 #define OP_GET        3
28 #define OP_GET_LEN    4
29 #define OP_SET        5
30 
31 /* Transport protocol error returns */
32 #define T_ESUCCESS    0
33 #define T_ENOENT    2
34 #define T_E2BIG        7
35 #define T_EMSGSIZE    40
36 
37 #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
38 
39 STATIC CONST CHAR8 mBhyveSig[4] = { 'B', 'H', 'Y', 'V' };
40 
41 STATIC BOOLEAN mBhyveFwCtlSupported = FALSE;
42 
43 STATIC INTN mBhyveFwCtlTxid = 0xa5;
44 
45 /* XXX Maybe a better inbuilt version of this ? */
46 struct BIoVec {
47   VOID        *Base;
48   UINTN        Len;
49 };
50 
51 struct MsgRxHdr {
52   UINT32    Sz;
53   UINT32    Op;
54   UINT32    TxId;
55   UINT32    Err;
56 };
57 
58 STATIC
59 RETURN_STATUS
60 EFIAPI
BhyveFwCtl_CvtErr(IN UINT32 errno)61 BhyveFwCtl_CvtErr (
62    IN UINT32    errno
63    )
64 {
65   RETURN_STATUS        Status;
66 
67   switch (errno) {
68   case T_ESUCCESS:
69     Status = RETURN_SUCCESS;
70     break;
71   case T_ENOENT:
72     Status = RETURN_NOT_FOUND;
73     break;
74   case T_E2BIG:
75     Status = RETURN_INVALID_PARAMETER;
76     break;
77   case T_EMSGSIZE:
78     Status = RETURN_BUFFER_TOO_SMALL;
79     break;
80   default:
81     Status = RETURN_PROTOCOL_ERROR;
82     break;
83   }
84 
85   return Status;
86 }
87 
88 STATIC
89 UINTN
90 EFIAPI
BIov_WLen(IN struct BIoVec b[])91 BIov_WLen (
92    IN struct BIoVec b[]
93    )
94 {
95   UINTN        i;
96   UINTN        tLen;
97 
98   tLen = 0;
99 
100   if (b != NULL) {
101     for (i = 0; b[i].Base != NULL; i++)
102       tLen += ROUNDUP (b[i].Len, sizeof(UINT32));
103   }
104 
105   return tLen;
106 }
107 
108 /**
109    Utility to send 1-3 bhyes of input as a 4-byte value
110    with trailing zeroes.
111  **/
112 STATIC
113 UINT32
BIov_Send_Rem(IN UINT32 * Data,IN UINTN Len)114 BIov_Send_Rem (
115    IN UINT32        *Data,
116    IN UINTN        Len
117    )
118 {
119   union {
120     UINT8    c[4];
121     UINT32    w;
122   } u;
123   UINT8        *cdata;
124   UINTN        i;
125 
126   cdata = (UINT8 *)Data;
127   u.w = 0;
128 
129   for (i = 0; i < Len; i++)
130     u.c[i] = *cdata++;
131 
132   return u.w;
133 }
134 
135 /**
136    Send a block of data out the i/o port as 4-byte quantities,
137    appending trailing zeroes on the last if required.
138  **/
139 STATIC
140 VOID
BIov_Send(IN char * Data,IN UINTN Len)141 BIov_Send (
142   IN char    *Data,
143   IN UINTN    Len
144   )
145 {
146   UINT32    *LData;
147 
148   LData = (UINT32 *)Data;
149 
150   while (Len > sizeof(UINT32)) {
151     IoWrite32 (FW_PORT, *LData++);
152     Len -= sizeof(UINT32);
153   }
154 
155   if (Len > 0) {
156     IoWrite32 (FW_PORT, BIov_Send_Rem (LData, Len));
157   }
158 }
159 
160 /**
161    Send data described by an array of iovecs out the i/o port.
162  **/
163 STATIC
164 VOID
BIov_SendAll(IN struct BIoVec b[])165 BIov_SendAll (
166    IN  struct BIoVec b[]
167    )
168 {
169   INTN        i;
170 
171   if (b != NULL) {
172     for (i = 0; b[i].Base; i++) {
173       BIov_Send (b[i].Base, b[i].Len);
174     }
175   }
176 }
177 
178 /**
179    Prepend the transport header to a block of data and send.
180  **/
181 STATIC
182 VOID
183 EFIAPI
BhyveFwCtl_MsgSend(IN UINTN OpCode,IN struct BIoVec Data[])184 BhyveFwCtl_MsgSend(
185    IN  UINTN    OpCode,
186    IN  struct BIoVec Data[]
187    )
188 {
189   struct BIoVec hIov[4];
190   UINT32        Hdr[3];
191   UINTN         i;
192 
193   /* Set up header as an iovec */
194   for (i = 0; i < 3; i++) {
195     hIov[i].Base = &Hdr[i];
196     hIov[i].Len  = sizeof(Hdr[0]);
197   }
198   hIov[i].Base = NULL;
199   hIov[i].Len = 0;
200 
201   /* Initialize header */
202   Hdr[0] = BIov_WLen (hIov) + BIov_WLen (Data);
203   Hdr[1] = OpCode;
204   Hdr[2] = mBhyveFwCtlTxid;
205 
206   /* Send header and data */
207   BIov_SendAll (hIov);
208   BIov_SendAll (Data);
209 }
210 
211 /**
212    Read a transport response and optional data from the i/o port.
213  **/
214 STATIC
215 RETURN_STATUS
216 EFIAPI
BhyveFwCtl_MsgRecv(OUT struct MsgRxHdr * Rhdr,OUT struct BIoVec Data[])217 BhyveFwCtl_MsgRecv(
218    OUT  struct MsgRxHdr    *Rhdr,
219    OUT  struct BIoVec    Data[]
220    )
221 {
222   RETURN_STATUS        Status;
223   UINT32        *Dp;
224   UINT32        Rd;
225   UINTN         remLen;
226   INTN            oLen, xLen;
227 
228   Rd = IoRead32 (FW_PORT);
229   if (Rd < sizeof(struct MsgRxHdr)) {
230     ;
231   }
232 
233   /* Read in header and setup initial error */
234   Rhdr->Sz   = Rd;
235   Rhdr->Op   = IoRead32 (FW_PORT);
236   Rhdr->TxId = IoRead32 (FW_PORT);
237   Rhdr->Err  = IoRead32 (FW_PORT);
238 
239   /* Convert transport errno into UEFI error status */
240   Status = BhyveFwCtl_CvtErr(Rhdr->Err);
241 
242   remLen = Rd - sizeof(struct MsgRxHdr);
243   xLen = 0;
244 
245   /*
246    * A few cases to handle:
247    *  - the user didn't supply a read buffer
248    *  - the buffer is too small for the response
249    *  - the response is zero-length
250    */
251   if (Data != NULL) {
252     Dp = (UINT32 *)Data[0].Base;
253     oLen = remLen;
254     if (remLen > Data[0].Len) {
255       Status = RETURN_BUFFER_TOO_SMALL;
256       xLen = remLen - Data[0].Len;
257       oLen = remLen = Data[0].Len;
258     }
259     while (remLen > 0) {
260       *Dp++ = IoRead32 (FW_PORT);
261       remLen -= sizeof(UINT32);
262     }
263     Data[0].Len = oLen;
264   } else {
265     /* No user data, but data returned - drop */
266     if (remLen > 0) {
267       Status = RETURN_BUFFER_TOO_SMALL;
268       xLen = remLen;
269     }
270   }
271 
272   /* Drop additional data */
273   while (xLen > 0) {
274     (void) IoRead32 (FW_PORT);
275     xLen -= sizeof(UINT32);
276   }
277 
278   return Status;
279 }
280 
281 
282 STATIC
283 RETURN_STATUS
284 EFIAPI
BhyveFwCtl_Msg(IN UINTN OpCode,IN struct BIoVec Sdata[],OUT struct BIoVec Rdata[])285 BhyveFwCtl_Msg(
286    IN   UINTN    OpCode,
287    IN   struct BIoVec Sdata[],
288    OUT  struct BIoVec Rdata[]
289    )
290 {
291   struct MsgRxHdr     Rh;
292   RETURN_STATUS        Status;
293 
294   Status = RETURN_SUCCESS;
295 
296   BhyveFwCtl_MsgSend (OpCode, Sdata);
297   Status = BhyveFwCtl_MsgRecv (&Rh, Rdata);
298 
299   mBhyveFwCtlTxid++;
300 
301   return Status;
302 }
303 
304 STATIC
305 RETURN_STATUS
306 EFIAPI
BhyveFwCtlGetLen(IN CONST CHAR8 * Name,IN OUT UINTN * Size)307 BhyveFwCtlGetLen (
308   IN   CONST CHAR8    *Name,
309   IN OUT  UINTN        *Size
310   )
311 {
312   struct BIoVec        Req[2], Resp[2];
313   RETURN_STATUS        Status;
314 
315   Req[0].Base = (VOID *)Name;
316   Req[0].Len  = AsciiStrLen (Name) + 1;
317   Req[1].Base = NULL;
318 
319   Resp[0].Base = Size;
320   Resp[0].Len  = sizeof(UINTN);
321   Resp[1].Base = NULL;
322 
323   Status = BhyveFwCtl_Msg (OP_GET_LEN, Req, Resp);
324 
325   return Status;
326 }
327 
328 #define FMAXSZ    1024
329 STATIC struct {
330   UINT64    fSize;
331   UINT32    fData[FMAXSZ];
332 } FwGetvalBuf;
333 
334 STATIC
335 RETURN_STATUS
336 EFIAPI
BhyveFwCtlGetVal(IN CONST CHAR8 * Name,OUT VOID * Item,IN OUT UINTN * Size)337 BhyveFwCtlGetVal (
338   IN   CONST CHAR8    *Name,
339   OUT  VOID        *Item,
340   IN OUT  UINTN        *Size
341   )
342 {
343   struct BIoVec        Req[2], Resp[2];
344   RETURN_STATUS        Status;
345 
346   /* Make sure temp buffer is larger than passed-in size */
347   if (*Size > sizeof(FwGetvalBuf.fData))
348       return RETURN_INVALID_PARAMETER;
349 
350   Req[0].Base = (VOID *)Name;
351   Req[0].Len  = AsciiStrLen(Name) + 1;
352   Req[1].Base = NULL;
353 
354   Resp[0].Base = &FwGetvalBuf;
355   Resp[0].Len  = sizeof(UINT64) + *Size;
356   Resp[1].Base = NULL;
357 
358   Status = BhyveFwCtl_Msg (OP_GET, Req, Resp);
359 
360   /*
361    * Copy out data on success (or on a truncated message).
362    * XXX This step can be eliminted with Msg() supporting
363    *     multiple iovecs.
364    */
365   if ((Status == RETURN_SUCCESS) || (Status == RETURN_BUFFER_TOO_SMALL)) {
366     *Size = FwGetvalBuf.fSize;
367     CopyMem (Item, FwGetvalBuf.fData, *Size);
368   }
369 
370   return Status;
371 }
372 
373 /**
374    Front end to the internal GET_LEN and GET protocols
375  **/
376 RETURN_STATUS
377 EFIAPI
BhyveFwCtlGet(IN CONST CHAR8 * Name,OUT VOID * Item,IN OUT UINTN * Size)378 BhyveFwCtlGet (
379   IN   CONST CHAR8    *Name,
380   OUT  VOID        *Item,
381   IN OUT  UINTN        *Size
382   )
383 {
384   RETURN_STATUS        Status;
385 
386   if (mBhyveFwCtlSupported == FALSE)
387     return RETURN_UNSUPPORTED;
388 
389   if (Item == NULL) {
390     Status = BhyveFwCtlGetLen (Name, Size);
391   } else {
392     Status = BhyveFwCtlGetVal (Name, Item, Size);
393   }
394 
395   return Status;
396 }
397 
398 
399 /**
400    Library initialization. Probe the host to see if the f/w ctl
401    interface is supported.
402  **/
403 RETURN_STATUS
404 EFIAPI
BhyveFwCtlInitialize(VOID)405 BhyveFwCtlInitialize (
406           VOID
407          )
408 {
409   UINTN        i;
410   UINT8        ch;
411 
412   DEBUG ((DEBUG_INFO, "FwCtlInitialize\n"));
413 
414   IoWrite16 (FW_PORT, 0x0000);
415   for (i = 0; i < 4; i++) {
416     ch = IoRead8 (FW_IPORT);
417     if (ch != mBhyveSig[i]) {
418       DEBUG ((DEBUG_INFO, "Host f/w sig mismatch %c/%c\n", ch, mBhyveSig[i]));
419       return RETURN_SUCCESS;
420     }
421   }
422 
423   mBhyveFwCtlSupported = TRUE;
424 
425   return RETURN_SUCCESS;
426 }
427