]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/BhyveFwCtlLib/BhyveFwCtlLib.c
OvmfPkg: Fix BhyveFwCtlLib build with VS2019
[mirror_edk2.git] / OvmfPkg / Library / BhyveFwCtlLib / BhyveFwCtlLib.c
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 INT32 mBhyveFwCtlTxid = 0xa5;
44
45 /* XXX Maybe a better inbuilt version of this ? */
46 struct BIoVec {
47 VOID *Base;
48 UINT32 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
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 UINT32
90 EFIAPI
91 BIov_WLen (
92 IN struct BIoVec b[]
93 )
94 {
95 UINT32 i;
96 UINT32 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 bytes of input as a 4-byte value
110 with trailing zeroes.
111 **/
112 STATIC
113 UINT32
114 BIov_Send_Rem (
115 IN UINT32 *Data,
116 IN UINT32 Len
117 )
118 {
119 union {
120 UINT8 c[4];
121 UINT32 w;
122 } u;
123 UINT8 *cdata;
124 UINT32 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
141 BIov_Send (
142 IN char *Data,
143 IN UINT32 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
165 BIov_SendAll (
166 IN struct BIoVec b[]
167 )
168 {
169 INT32 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
184 BhyveFwCtl_MsgSend(
185 IN UINT32 OpCode,
186 IN struct BIoVec Data[]
187 )
188 {
189 struct BIoVec hIov[4];
190 UINT32 Hdr[3];
191 UINT32 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] = (UINT32)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
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 UINT32 remLen;
226 INT32 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
285 BhyveFwCtl_Msg(
286 IN UINT32 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
307 BhyveFwCtlGetLen (
308 IN CONST CHAR8 *Name,
309 IN OUT UINT32 *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 = (UINT32)AsciiStrLen (Name) + 1;
317 Req[1].Base = NULL;
318
319 Resp[0].Base = Size;
320 Resp[0].Len = sizeof(UINT32);
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
337 BhyveFwCtlGetVal (
338 IN CONST CHAR8 *Name,
339 OUT VOID *Item,
340 IN OUT UINT32 *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 = (UINT32)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 = (UINT32)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
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, (UINT32*)Size);
391 } else {
392 Status = BhyveFwCtlGetVal (Name, Item, (UINT32*)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
405 BhyveFwCtlInitialize (
406 VOID
407 )
408 {
409 UINT32 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 }