Date   

[PATCH v5 1/1] ShellPkg/DynamicCommand: add HttpDynamicCommand

Vladimir Olovyannikov
 

Introduce an http client utilizing EDK2 HTTP protocol, to
allow fast image downloading from http/https servers.
HTTP download speed is usually faster than tftp.
The client is based on the same approach as tftp dynamic command, and
uses the same UEFI Shell command line parameters. This makes it easy
integrating http into existing UEFI Shell scripts.
Note that to enable HTTP download, feature Pcd
gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections must
be set to TRUE.
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3D2860

Signed-off-by: Vladimir Olovyannikov <vladimir.olovyannikov@...>
CC: Samer El-Haj-Mahmoud <Samer.El-Haj-Mahmoud@...>
CC: Laszlo Ersek <lersek@...>
Cc: Zhichao Gao <zhichao.gao@...>
Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Ray Ni <ray.ni@...>
Cc: Liming Gao <liming.gao@...>
Cc: Nd <nd@...>
---
CryptoPkg/Library/OpensslLib/openssl | 2 +-
.../DynamicCommand/HttpDynamicCommand/Http.c | 1715 +++++++++++++++++
.../DynamicCommand/HttpDynamicCommand/Http.h | 89 +
.../HttpDynamicCommand/Http.uni | 117 ++
.../HttpDynamicCommand/HttpApp.c | 53 +
.../HttpDynamicCommand/HttpApp.inf | 58 +
.../HttpDynamicCommand/HttpDynamicCommand.c | 134 ++
.../HttpDynamicCommand/HttpDynamicCommand.inf | 63 +
ShellPkg/Include/Guid/ShellLibHiiGuid.h | 5 +
ShellPkg/ShellPkg.dec | 1 +
ShellPkg/ShellPkg.dsc | 5 +
11 files changed, 2241 insertions(+), 1 deletion(-)
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/Http.h
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/Http.uni
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.c
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.inf
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicC=
ommand.c
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicC=
ommand.inf

diff --git a/CryptoPkg/Library/OpensslLib/openssl b/CryptoPkg/Library/Opens=
slLib/openssl
index e2e09d9fba11..c3656cc594da 160000
--- a/CryptoPkg/Library/OpensslLib/openssl
+++ b/CryptoPkg/Library/OpensslLib/openssl
@@ -1 +1 @@
-Subproject commit e2e09d9fba1187f8d6aafaa34d4172f56f1ffb72
+Subproject commit c3656cc594daac8167721dde7220f0e59ae146fc
diff --git a/ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c b/ShellPkg/D=
ynamicCommand/HttpDynamicCommand/Http.c
new file mode 100644
index 000000000000..0565b07c3570
--- /dev/null
+++ b/ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c
@@ -0,0 +1,1715 @@
+/** @file=0D
+ The implementation for the 'http' Shell command.=0D
+=0D
+ Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>=0D
+ Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved. <BR>=
=0D
+ (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>=0D
+ Copyright (c) 2020, Broadcom. All rights reserved. <BR>=0D
+=0D
+ SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+**/=0D
+=0D
+#include "Http.h"=0D
+=0D
+#define IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH 32=0D
+EFI_HII_HANDLE mHttpHiiHandle;=0D
+=0D
+/*=0D
+ Constant strings and definitions related to the message=0D
+ indicating the amount of progress in the dowloading of a HTTP file.=0D
+*/=0D
+=0D
+// Number of steps in the progression slider=0D
+#define HTTP_PROGRESS_SLIDER_STEPS \=0D
+ ((sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) - 3)=0D
+=0D
+// Size in number of characters plus one (final zero) of the message to=0D
+// indicate the progress of an HTTP download. The format is "[(progress sl=
ider:=0D
+// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". The=
re=0D
+// are thus the number of characters in HTTP_PROGR_FRAME[] plus 11 charact=
ers=0D
+// (2 // spaces, "Kb" and seven characters for the number of KBytes).=0D
+#define HTTP_PROGRESS_MESSAGE_SIZE \=0D
+ ((sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) + 12)=0D
+=0D
+//=0D
+// Buffer size. Note that larger buffer does not mean better speed!=0D
+//=0D
+#define DEFAULT_BUF_SIZE SIZE_32KB=0D
+#define MAX_BUF_SIZE SIZE_4MB=0D
+=0D
+#define MIN_PARAM_COUNT 2=0D
+#define MAX_PARAM_COUNT 4=0D
+=0D
+#define TIMER_MAX_TIMEOUT_S 10=0D
+=0D
+// File name to use when URI ends with "/"=0D
+#define DEFAULT_HTML_FILE L"index.html"=0D
+#define DEFAULT_HTTP_PROTO L"http"=0D
+=0D
+// String to delete the HTTP progress message to be able to update it :=0D
+// (HTTP_PROGRESS_MESSAGE_SIZE-1) '\b'=0D
+#define HTTP_PROGRESS_DEL \=0D
+ L"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b=
\=0D
+\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"=0D
+=0D
+#define HTTP_KB L"\b\b\b\b\b\b\b\b\b\b"=0D
+// Frame for the progression slider=0D
+#define HTTP_PROGR_FRAME L"[ ]"=
=0D
+=0D
+// String descriptions for server errors=0D
+STATIC CONST CHAR16 *ErrStatusDesc[] =3D=0D
+{=0D
+ L"400 Bad Request",=0D
+ L"401 Unauthorized",=0D
+ L"402 Payment required",=0D
+ L"403 Forbidden",=0D
+ L"404 Not Found",=0D
+ L"405 Method not allowed",=0D
+ L"406 Not acceptable",=0D
+ L"407 Proxy authentication required",=0D
+ L"408 Request time out",=0D
+ L"409 Conflict",=0D
+ L"410 Gone",=0D
+ L"411 Length required",=0D
+ L"412 Precondition failed",=0D
+ L"413 Request entity too large",=0D
+ L"414 Request URI to large",=0D
+ L"415 Unsupported media type",=0D
+ L"416 Requested range not satisfied",=0D
+ L"417 Expectation failed",=0D
+ L"500 Internal server error",=0D
+ L"501 Not implemented",=0D
+ L"502 Bad gateway",=0D
+ L"503 Service unavailable",=0D
+ L"504 Gateway timeout",=0D
+ L"505 HTTP version not supported"=0D
+};=0D
+=0D
+// Local File Handle=0D
+STATIC SHELL_FILE_HANDLE mFileHandle =3D NULL;=0D
+=0D
+// Path of the local file, Unicode encoded=0D
+STATIC CONST CHAR16 *mLocalFilePath;=0D
+=0D
+STATIC BOOLEAN gRequestCallbackComplete =3D FALSE;=0D
+STATIC BOOLEAN gResponseCallbackComplete =3D FALSE;=0D
+=0D
+STATIC BOOLEAN gHttpError;=0D
+=0D
+/**=0D
+ Cleans off leading and trailing spaces and tabs.=0D
+=0D
+ @param[in] String pointer to the string to trim them off.=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+TrimSpaces (=0D
+ IN CHAR16 **String=0D
+ )=0D
+{=0D
+ ASSERT(String !=3D NULL);=0D
+ ASSERT(*String!=3D NULL);=0D
+ //=0D
+ // Remove any spaces and tabs at the beginning of the (*String).=0D
+ //=0D
+ while (((*String)[0] =3D=3D L' ') || ((*String)[0] =3D=3D L'\t')) {=0D
+ CopyMem (=0D
+ (*String),=0D
+ (*String) + 1,=0D
+ StrSize ((*String)) - sizeof((*String)[0])=0D
+ );=0D
+ }=0D
+=0D
+ //=0D
+ // Remove any spaces and tabs at the end of the (*String).=0D
+ //=0D
+ while ((StrLen (*String) > 0) &&=0D
+ (((*String)[StrLen ((*String)) - 1] =3D=3D L' ') ||=0D
+ ((*String)[StrLen ((*String)) - 1] =3D=3D L'\t'))=0D
+ ) {=0D
+ (*String)[StrLen ((*String)) - 1] =3D CHAR_NULL;=0D
+ }=0D
+=0D
+ return (EFI_SUCCESS);=0D
+}=0D
+=0D
+=0D
+/*=0D
+ * Callbacks for request and response.=0D
+ * We just acknowledge that operation has completed here.=0D
+ */=0D
+STATIC=0D
+VOID=0D
+EFIAPI=0D
+RequestCallback (=0D
+ IN EFI_EVENT Event,=0D
+ IN VOID *Context=0D
+)=0D
+{=0D
+ gRequestCallbackComplete =3D TRUE;=0D
+}=0D
+=0D
+STATIC=0D
+VOID=0D
+EFIAPI=0D
+ResponseCallback (=0D
+ IN EFI_EVENT Event,=0D
+ IN VOID *Context=0D
+)=0D
+{=0D
+ gResponseCallbackComplete =3D TRUE;=0D
+}=0D
+=0D
+=0D
+/**=0D
+ Check and convert the UINT16 option values of the 'http' command=0D
+=0D
+ @param[in] ValueStr Value as an Unicode encoded string=0D
+ @param[out] Value UINT16 value=0D
+=0D
+ @return TRUE The value was returned.=0D
+ @return FALSE A parsing error occured.=0D
+**/=0D
+STATIC=0D
+BOOLEAN=0D
+StringToUint16 (=0D
+ IN CONST CHAR16 *ValueStr,=0D
+ OUT UINT16 *Value=0D
+ );=0D
+=0D
+/**=0D
+ Get the name of the NIC.=0D
+=0D
+ @param[in] ControllerHandle The network physical device handle.=0D
+ @param[in] NicNumber The network physical device number.=0D
+ @param[out] NicName Address where to store the NIC name.=0D
+ The memory area has to be at least=0D
+ IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH=0D
+ double byte wide.=0D
+=0D
+ @return EFI_SUCCESS The name of the NIC was returned.=0D
+ @return Others The creation of the child for the Managed=0D
+ Network Service failed or the opening of=0D
+ the Managed Network Protocol failed or=0D
+ the operational parameters for the=0D
+ Managed Network Protocol could not be=0D
+ read.=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+GetNicName (=0D
+ IN EFI_HANDLE ControllerHandle,=0D
+ IN UINTN NicNumber,=0D
+ OUT CHAR16 *NicName=0D
+ );=0D
+=0D
+/**=0D
+ Create a child for the service identified by its service binding protoco=
l GUID=0D
+ and get from the child the interface of the protocol identified by its G=
UID.=0D
+=0D
+ @param[in] ControllerHandle Controller handle.=0D
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID o=
f the=0D
+ service to be created.=0D
+ @param[in] ProtocolGuid GUID of the protocol to be open=
.=0D
+ @param[out] ChildHandle Address where the handler of th=
e=0D
+ created child is returned. NULL=
is=0D
+ returned in case of error.=0D
+ @param[out] Interface Address where a pointer to the=
=0D
+ protocol interface is returned =
in=0D
+ case of success.=0D
+=0D
+ @return EFI_SUCCESS The child was created and the protocol opened.=0D
+ @return Others Either the creation of the child or the opening=0D
+ of the protocol failed.=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+CreateServiceChildAndOpenProtocol (=0D
+ IN EFI_HANDLE ControllerHandle,=0D
+ IN EFI_GUID *ServiceBindingProtocolGuid,=0D
+ IN EFI_GUID *ProtocolGuid,=0D
+ OUT EFI_HANDLE *ChildHandle,=0D
+ OUT VOID **Interface=0D
+ );=0D
+=0D
+/**=0D
+ Close the protocol identified by its GUID on the child handle of the ser=
vice=0D
+ identified by its service binding protocol GUID, then destroy the child=
=0D
+ handle.=0D
+=0D
+ @param[in] ControllerHandle Controller handle.=0D
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of=
the=0D
+ service to be destroyed.=0D
+ @param[in] ProtocolGuid GUID of the protocol to be close=
d.=0D
+ @param[in] ChildHandle Handle of the child to be destro=
yed.=0D
+=0D
+**/=0D
+STATIC=0D
+VOID=0D
+CloseProtocolAndDestroyServiceChild (=0D
+ IN EFI_HANDLE ControllerHandle,=0D
+ IN EFI_GUID *ServiceBindingProtocolGuid,=0D
+ IN EFI_GUID *ProtocolGuid,=0D
+ IN EFI_HANDLE ChildHandle=0D
+ );=0D
+=0D
+=0D
+/**=0D
+ Worker function that download the data of a file from an HTTP server giv=
en=0D
+ the path of the file and its size.=0D
+=0D
+ @param[in] Context A pointer to the download context.=0D
+=0D
+ @retval EFI_SUCCESS The file was downloaded.=0D
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.=0D
+ @retval Others The downloading of the file=0D
+ from the server failed.=0D
+=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+DownloadFile (=0D
+ IN DOWNLOAD_CONTEXT *Context=0D
+ );=0D
+=0D
+STATIC CONST SHELL_PARAM_ITEM ParamList[] =3D {=0D
+ {L"-i", TypeValue},=0D
+ {L"-k", TypeFlag},=0D
+ {L"-l", TypeValue},=0D
+ {L"-s", TypeValue},=0D
+ {L"-t", TypeValue},=0D
+ {NULL , TypeMax}=0D
+ };=0D
+=0D
+/**=0D
+ Function for 'http' command.=0D
+=0D
+ @param[in] ImageHandle Handle to the Image (NULL if Internal).=0D
+ @param[in] SystemTable Pointer to the System Table (NULL if Internal).=
=0D
+=0D
+ @return SHELL_SUCCESS The 'http' command completed successfu=
lly.=0D
+ @return SHELL_ABORTED The Shell Library initialization faile=
d.=0D
+ @return SHELL_INVALID_PARAMETER At least one of the command's argument=
s is=0D
+ not valid.=0D
+ @return SHELL_OUT_OF_RESOURCES A memory allocation failed.=0D
+ @return SHELL_NOT_FOUND Network Interface Card not found.=0D
+ @return SHELL_UNSUPPORTED Command was valid, but the server retu=
rned=0D
+ a status code indicating some error.=0D
+ Examine the file requested for error b=
ody.=0D
+=0D
+**/=0D
+SHELL_STATUS=0D
+RunHttp (=0D
+ IN EFI_HANDLE ImageHandle,=0D
+ IN EFI_SYSTEM_TABLE *SystemTable=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ LIST_ENTRY *CheckPackage;=0D
+ UINTN ParamCount;=0D
+ UINTN HandleCount;=0D
+ UINTN NicNumber;=0D
+ UINTN InitialSize;=0D
+ UINTN ParamOffset;=0D
+ UINTN StartSize;=0D
+ CHAR16 *ProblemParam;=0D
+ CHAR16 NicName[IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH];=
=0D
+ CHAR16 *Walker1;=0D
+ CHAR16 *VStr;=0D
+ CONST CHAR16 *UserNicName;=0D
+ CONST CHAR16 *ValueStr;=0D
+ CONST CHAR16 *RemoteFilePath;=0D
+ CONST CHAR16 *Walker;=0D
+ EFI_HTTP_CONFIG_DATA HttpConfigData;=0D
+ EFI_HTTPv4_ACCESS_POINT IPv4Node;=0D
+ EFI_HANDLE *Handles;=0D
+ EFI_HANDLE ControllerHandle;=0D
+ EFI_HANDLE HttpChildHandle;=0D
+ DOWNLOAD_CONTEXT Context;=0D
+ BOOLEAN NicFound;=0D
+=0D
+ ProblemParam =3D NULL;=0D
+ RemoteFilePath =3D NULL;=0D
+ NicFound =3D FALSE;=0D
+ Handles =3D NULL;=0D
+=0D
+ //=0D
+ // Initialize the Shell library (we must be in non-auto-init...)=0D
+ //=0D
+ ParamOffset =3D 0;=0D
+ gHttpError =3D FALSE;=0D
+=0D
+ Status =3D ShellInitialize ();=0D
+ if (EFI_ERROR (Status)) {=0D
+ ASSERT_EFI_ERROR (Status);=0D
+ return SHELL_ABORTED;=0D
+ }=0D
+=0D
+ ZeroMem (&Context, sizeof (Context));=0D
+=0D
+ //=0D
+ // Parse the command line.=0D
+ //=0D
+ Status =3D ShellCommandLineParse (=0D
+ ParamList,=0D
+ &CheckPackage,=0D
+ &ProblemParam,=0D
+ TRUE);=0D
+ if (EFI_ERROR (Status)) {=0D
+ if ((Status =3D=3D EFI_VOLUME_CORRUPTED) &&=0D
+ (ProblemParam !=3D NULL) ) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM), mHttpHiiHandle,=0D
+ HTTP_APP_NAME, ProblemParam=0D
+ );=0D
+ SHELL_FREE_NON_NULL (ProblemParam);=0D
+ } else {=0D
+ ASSERT (FALSE);=0D
+ }=0D
+=0D
+ goto Error;=0D
+ }=0D
+=0D
+ //=0D
+ // Check the number of parameters=0D
+ //=0D
+ Status =3D EFI_INVALID_PARAMETER;=0D
+=0D
+ ParamCount =3D ShellCommandLineGetCount (CheckPackage);=0D
+ if (ParamCount > MAX_PARAM_COUNT) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY),=0D
+ mHttpHiiHandle, HTTP_APP_NAME=0D
+ );=0D
+ goto Error;=0D
+ }=0D
+=0D
+ if (ParamCount < MIN_PARAM_COUNT) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW),=0D
+ mHttpHiiHandle, HTTP_APP_NAME=0D
+ );=0D
+ goto Error;=0D
+ }=0D
+=0D
+ ZeroMem (&HttpConfigData, sizeof (HttpConfigData));=0D
+ ZeroMem (&IPv4Node, sizeof (IPv4Node));=0D
+ IPv4Node.UseDefaultAddress =3D TRUE;=0D
+=0D
+ HttpConfigData.HttpVersion =3D HttpVersion11;=0D
+ HttpConfigData.AccessPoint.IPv4Node =3D &IPv4Node;=0D
+=0D
+ //=0D
+ // Get the host address (not necessarily IPv4 format)=0D
+ //=0D
+ ValueStr =3D ShellCommandLineGetRawValue (CheckPackage, 1);=0D
+ if (!ValueStr) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV), mHttpHiiHandle,=0D
+ HTTP_APP_NAME, ValueStr=0D
+ );=0D
+ goto Error;=0D
+ } else {=0D
+ StartSize =3D 0;=0D
+ if (!StrStr (ValueStr, L"://")) {=0D
+ Context.ServerAddrAndProto =3D StrnCatGrow (=0D
+ &Context.ServerAddrAndProto,=0D
+ &StartSize,=0D
+ DEFAULT_HTTP_PROTO,=0D
+ StrLen (DEFAULT_HTTP_PROTO)=0D
+ );=0D
+ Context.ServerAddrAndProto =3D StrnCatGrow (=0D
+ &Context.ServerAddrAndProto,=0D
+ &StartSize,=0D
+ L"://",=0D
+ StrLen (L"://")=0D
+ );=0D
+ VStr =3D (CHAR16 *)ValueStr;=0D
+ } else {=0D
+ VStr =3D StrStr (ValueStr, L"://") + StrLen (L"://");=0D
+ }=0D
+=0D
+ for (Walker1 =3D VStr; *Walker1; Walker1++) {=0D
+ if (*Walker1 =3D=3D L'/') {=0D
+ break;=0D
+ }=0D
+ }=0D
+=0D
+ if (*Walker1 =3D=3D L'/') {=0D
+ ParamOffset =3D 1;=0D
+ RemoteFilePath =3D Walker1;=0D
+ }=0D
+=0D
+ Context.ServerAddrAndProto =3D StrnCatGrow (=0D
+ &Context.ServerAddrAndProto,=0D
+ &StartSize,=0D
+ ValueStr,=0D
+ StrLen (ValueStr) - StrLen (Walker1)=0D
+ );=0D
+ if (!Context.ServerAddrAndProto) {=0D
+ Status =3D EFI_OUT_OF_RESOURCES;=0D
+ goto Error;=0D
+ }=0D
+ }=0D
+=0D
+ if (!RemoteFilePath) {=0D
+ RemoteFilePath =3D ShellCommandLineGetRawValue (CheckPackage, 2);=0D
+ if (!RemoteFilePath) {=0D
+ // If no path given, assume just "/"=0D
+ RemoteFilePath =3D L"/";=0D
+ }=0D
+ }=0D
+=0D
+ TrimSpaces ((CHAR16 **)&RemoteFilePath);=0D
+=0D
+ if (ParamCount =3D=3D MAX_PARAM_COUNT - ParamOffset) {=0D
+ mLocalFilePath =3D ShellCommandLineGetRawValue (=0D
+ CheckPackage,=0D
+ MAX_PARAM_COUNT - 1 - ParamOffset);=0D
+ } else {=0D
+ Walker =3D RemoteFilePath + StrLen (RemoteFilePath);=0D
+ while ((--Walker) >=3D RemoteFilePath) {=0D
+ if ((*Walker =3D=3D L'\\') ||=0D
+ (*Walker =3D=3D L'/' ) ) {=0D
+ break;=0D
+ }=0D
+ }=0D
+=0D
+ mLocalFilePath =3D Walker + 1;=0D
+ }=0D
+=0D
+ if (!StrLen (mLocalFilePath)) {=0D
+ mLocalFilePath =3D DEFAULT_HTML_FILE;=0D
+ }=0D
+=0D
+ InitialSize =3D 0;=0D
+ Context.URI =3D StrnCatGrow (=0D
+ &Context.URI,=0D
+ &InitialSize,=0D
+ RemoteFilePath,=0D
+ StrLen (RemoteFilePath)=0D
+ );=0D
+ if (!Context.URI) {=0D
+ Status =3D EFI_OUT_OF_RESOURCES;=0D
+ goto Error;=0D
+ }=0D
+=0D
+ //=0D
+ // Get the name of the Network Interface Card to be used if any.=0D
+ //=0D
+ UserNicName =3D ShellCommandLineGetValue (CheckPackage, L"-i");=0D
+=0D
+ ValueStr =3D ShellCommandLineGetValue (CheckPackage, L"-l");=0D
+ if (ValueStr !=3D NULL) {=0D
+ if (!StringToUint16 (=0D
+ ValueStr,=0D
+ &HttpConfigData.AccessPoint.IPv4Node->LocalPort=0D
+ )=0D
+ ) {=0D
+ goto Error;=0D
+ }=0D
+ }=0D
+=0D
+ Context.BufferSize =3D DEFAULT_BUF_SIZE;=0D
+=0D
+ ValueStr =3D ShellCommandLineGetValue (CheckPackage, L"-s");=0D
+ if (ValueStr !=3D NULL) {=0D
+ Context.BufferSize =3D ShellStrToUintn (ValueStr);=0D
+ if (!Context.BufferSize || Context.BufferSize > MAX_BUF_SIZE) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),=0D
+ mHttpHiiHandle, HTTP_APP_NAME, ValueStr=0D
+ );=0D
+ goto Error;=0D
+ }=0D
+ }=0D
+=0D
+ ValueStr =3D ShellCommandLineGetValue (CheckPackage, L"-t");=0D
+ if (ValueStr !=3D NULL) {=0D
+ HttpConfigData.TimeOutMillisec =3D (UINT32)ShellStrToUintn (ValueStr);=
=0D
+ }=0D
+=0D
+ //=0D
+ // Locate all HTTP Service Binding protocols=0D
+ //=0D
+ Status =3D gBS->LocateHandleBuffer (=0D
+ ByProtocol,=0D
+ &gEfiManagedNetworkServiceBindingProtocolGuid,=0D
+ NULL,=0D
+ &HandleCount,=0D
+ &Handles=0D
+ );=0D
+ if (EFI_ERROR (Status) || (HandleCount =3D=3D 0)) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_NO_NIC), mHttpHiiHandle=0D
+ );=0D
+ if (!EFI_ERROR (Status)) {=0D
+ Status =3D EFI_NOT_FOUND;=0D
+ }=0D
+=0D
+ goto Error;=0D
+ }=0D
+=0D
+ Status =3D EFI_NOT_FOUND;=0D
+=0D
+ for (NicNumber =3D 0;=0D
+ (NicNumber < HandleCount) && (Status !=3D EFI_SUCCESS);=0D
+ NicNumber++) {=0D
+ ControllerHandle =3D Handles[NicNumber];=0D
+=0D
+ Status =3D GetNicName (ControllerHandle, NicNumber, NicName);=0D
+ if (EFI_ERROR (Status)) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_NIC_NAME),=0D
+ mHttpHiiHandle, NicNumber, Status=0D
+ );=0D
+ continue;=0D
+ }=0D
+=0D
+ if (UserNicName !=3D NULL) {=0D
+ if (StrCmp (NicName, UserNicName) !=3D 0) {=0D
+ Status =3D EFI_NOT_FOUND;=0D
+ continue;=0D
+ }=0D
+ NicFound =3D TRUE;=0D
+ }=0D
+=0D
+ Status =3D CreateServiceChildAndOpenProtocol (=0D
+ ControllerHandle,=0D
+ &gEfiHttpServiceBindingProtocolGuid,=0D
+ &gEfiHttpProtocolGuid,=0D
+ &HttpChildHandle,=0D
+ (VOID**)&Context.Http=0D
+ );=0D
+ if (EFI_ERROR (Status)) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_OPEN_PROTOCOL),=0D
+ mHttpHiiHandle, NicName, Status=0D
+ );=0D
+ continue;=0D
+ }=0D
+=0D
+ Status =3D Context.Http->Configure (Context.Http, &HttpConfigData);=0D
+ if (EFI_ERROR (Status)) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_CONFIGURE),=0D
+ mHttpHiiHandle, NicName, Status=0D
+ );=0D
+ continue;=0D
+ }=0D
+=0D
+ Context.Flags =3D 0;=0D
+ if (ShellCommandLineGetFlag (CheckPackage, L"-m")) {=0D
+ Context.Flags |=3D DL_FLAG_TIME;=0D
+ }=0D
+=0D
+ if (ShellCommandLineGetFlag (CheckPackage, L"-k")) {=0D
+ Context.Flags |=3D DL_FLAG_KEEP_BAD;=0D
+ }=0D
+=0D
+ Status =3D DownloadFile (&Context);=0D
+=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_CRLF), mHttpHiiHandle=0D
+ );=0D
+=0D
+ CloseProtocolAndDestroyServiceChild (=0D
+ ControllerHandle,=0D
+ &gEfiHttpServiceBindingProtocolGuid,=0D
+ &gEfiHttpProtocolGuid,=0D
+ HttpChildHandle=0D
+ );=0D
+=0D
+ if (EFI_ERROR (Status)) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_DOWNLOAD),=0D
+ mHttpHiiHandle, RemoteFilePath, NicName, Status=0D
+ );=0D
+ // If a user aborted the operation, do not try another controller.=0D
+ if (Status =3D=3D EFI_ABORTED) {=0D
+ goto Error;=0D
+ }=0D
+ }=0D
+=0D
+ if (gHttpError) {=0D
+ //=0D
+ // This is not related to connection, so no need to repeat with=0D
+ // another interface.=0D
+ //=0D
+ break;=0D
+ }=0D
+ }=0D
+=0D
+ if ((UserNicName !=3D NULL) && (!NicFound)) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_NIC_NOT_FOUND),=0D
+ mHttpHiiHandle, UserNicName=0D
+ );=0D
+ }=0D
+=0D
+Error:=0D
+ ShellCommandLineFreeVarList (CheckPackage);=0D
+ SHELL_FREE_NON_NULL (Handles);=0D
+ SHELL_FREE_NON_NULL (Context.ServerAddrAndProto);=0D
+ SHELL_FREE_NON_NULL (Context.URI);=0D
+=0D
+ return Status & ~MAX_BIT;=0D
+}=0D
+=0D
+/**=0D
+ Check and convert the UINT16 option values of the 'http' command=0D
+=0D
+ @param[in] ValueStr Value as an Unicode encoded string=0D
+ @param[out] Value UINT16 value=0D
+=0D
+ @return TRUE The value was returned.=0D
+ @return FALSE A parsing error occured.=0D
+**/=0D
+STATIC=0D
+BOOLEAN=0D
+StringToUint16 (=0D
+ IN CONST CHAR16 *ValueStr,=0D
+ OUT UINT16 *Value=0D
+ )=0D
+{=0D
+ UINTN Val;=0D
+=0D
+ Val =3D ShellStrToUintn (ValueStr);=0D
+ if (Val > MAX_UINT16) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_PARAM_INV),=0D
+ mHttpHiiHandle, HTTP_APP_NAME, ValueStr=0D
+ );=0D
+ return FALSE;=0D
+ }=0D
+=0D
+ *Value =3D (UINT16)Val;=0D
+ return TRUE;=0D
+}=0D
+=0D
+/**=0D
+ Get the name of the NIC.=0D
+=0D
+ @param[in] ControllerHandle The network physical device handle.=0D
+ @param[in] NicNumber The network physical device number.=0D
+ @param[out] NicName Address where to store the NIC name.=0D
+ The memory area has to be at least=0D
+ IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH=0D
+ double byte wide.=0D
+=0D
+ @return EFI_SUCCESS The name of the NIC was returned.=0D
+ @return Others The creation of the child for the Managed=0D
+ Network Service failed or the opening of=0D
+ the Managed Network Protocol failed or=0D
+ the operational parameters for the=0D
+ Managed Network Protocol could not be=0D
+ read.=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+GetNicName (=0D
+ IN EFI_HANDLE ControllerHandle,=0D
+ IN UINTN NicNumber,=0D
+ OUT CHAR16 *NicName=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ EFI_HANDLE MnpHandle;=0D
+ EFI_MANAGED_NETWORK_PROTOCOL *Mnp;=0D
+ EFI_SIMPLE_NETWORK_MODE SnpMode;=0D
+=0D
+ Status =3D CreateServiceChildAndOpenProtocol (=0D
+ ControllerHandle,=0D
+ &gEfiManagedNetworkServiceBindingProtocolGuid,=0D
+ &gEfiManagedNetworkProtocolGuid,=0D
+ &MnpHandle,=0D
+ (VOID**)&Mnp=0D
+ );=0D
+ if (EFI_ERROR (Status)) {=0D
+ goto Error;=0D
+ }=0D
+=0D
+ Status =3D Mnp->GetModeData (Mnp, NULL, &SnpMode);=0D
+ if (EFI_ERROR (Status) && (Status !=3D EFI_NOT_STARTED)) {=0D
+ goto Error;=0D
+ }=0D
+=0D
+ UnicodeSPrint (=0D
+ NicName,=0D
+ IP4_CONFIG2_INTERFACE_INFO_NAME_LENGTH,=0D
+ SnpMode.IfType =3D=3D NET_IFTYPE_ETHERNET ?=0D
+ L"eth%d" :=0D
+ L"unk%d",=0D
+ NicNumber=0D
+ );=0D
+=0D
+ Status =3D EFI_SUCCESS;=0D
+=0D
+Error:=0D
+=0D
+ if (MnpHandle !=3D NULL) {=0D
+ CloseProtocolAndDestroyServiceChild (=0D
+ ControllerHandle,=0D
+ &gEfiManagedNetworkServiceBindingProtocolGuid,=0D
+ &gEfiManagedNetworkProtocolGuid,=0D
+ MnpHandle=0D
+ );=0D
+ }=0D
+=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ Create a child for the service identified by its service binding protoco=
l GUID=0D
+ and get from the child the interface of the protocol identified by its G=
UID.=0D
+=0D
+ @param[in] ControllerHandle Controller handle.=0D
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID o=
f the=0D
+ service to be created.=0D
+ @param[in] ProtocolGuid GUID of the protocol to be open=
.=0D
+ @param[out] ChildHandle Address where the handler of th=
e=0D
+ created child is returned. NULL=
is=0D
+ returned in case of error.=0D
+ @param[out] Interface Address where a pointer to the=
=0D
+ protocol interface is returned =
in=0D
+ case of success.=0D
+=0D
+ @return EFI_SUCCESS The child was created and the protocol opened.=0D
+ @return Others Either the creation of the child or the opening=0D
+ of the protocol failed.=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+CreateServiceChildAndOpenProtocol (=0D
+ IN EFI_HANDLE ControllerHandle,=0D
+ IN EFI_GUID *ServiceBindingProtocolGuid,=0D
+ IN EFI_GUID *ProtocolGuid,=0D
+ OUT EFI_HANDLE *ChildHandle,=0D
+ OUT VOID **Interface=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+=0D
+ *ChildHandle =3D NULL;=0D
+ Status =3D NetLibCreateServiceChild (=0D
+ ControllerHandle,=0D
+ gImageHandle,=0D
+ ServiceBindingProtocolGuid,=0D
+ ChildHandle=0D
+ );=0D
+ if (!EFI_ERROR (Status)) {=0D
+ Status =3D gBS->OpenProtocol (=0D
+ *ChildHandle,=0D
+ ProtocolGuid,=0D
+ Interface,=0D
+ gImageHandle,=0D
+ ControllerHandle,=0D
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL=0D
+ );=0D
+ if (EFI_ERROR (Status)) {=0D
+ NetLibDestroyServiceChild (=0D
+ ControllerHandle,=0D
+ gImageHandle,=0D
+ ServiceBindingProtocolGuid,=0D
+ *ChildHandle=0D
+ );=0D
+ *ChildHandle =3D NULL;=0D
+ }=0D
+ }=0D
+=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ Close the protocol identified by its GUID on the child handle of the ser=
vice=0D
+ identified by its service binding protocol GUID, then destroy the child=
=0D
+ handle.=0D
+=0D
+ @param[in] ControllerHandle Controller handle.=0D
+ @param[in] ServiceBindingProtocolGuid Service binding protocol GUID of=
the=0D
+ service to be destroyed.=0D
+ @param[in] ProtocolGuid GUID of the protocol to be close=
d.=0D
+ @param[in] ChildHandle Handle of the child to be destro=
yed.=0D
+=0D
+**/=0D
+STATIC=0D
+VOID=0D
+CloseProtocolAndDestroyServiceChild (=0D
+ IN EFI_HANDLE ControllerHandle,=0D
+ IN EFI_GUID *ServiceBindingProtocolGuid,=0D
+ IN EFI_GUID *ProtocolGuid,=0D
+ IN EFI_HANDLE ChildHandle=0D
+ )=0D
+{=0D
+ gBS->CloseProtocol (=0D
+ ChildHandle,=0D
+ ProtocolGuid,=0D
+ gImageHandle,=0D
+ ControllerHandle=0D
+ );=0D
+=0D
+ NetLibDestroyServiceChild (=0D
+ ControllerHandle,=0D
+ gImageHandle,=0D
+ ServiceBindingProtocolGuid,=0D
+ ChildHandle=0D
+ );=0D
+}=0D
+=0D
+/**=0D
+ Wait until operation completes. Completion is indicated by=0D
+ setting of an appropriate variable.=0D
+=0D
+ @param[in] Context A pointer to the HTTP download context.=
=0D
+ @param[in] CallBackComplete A pointer to the callback completion=0D
+ variable set by the callback.=0D
+=0D
+ @return EFI_SUCCESS Callback signalled completion.=0D
+ @return EFI_TIMEOUT Timed out waiting for completion.=0D
+ @return Others Error waiting for completion.=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+WaitForCompletion (=0D
+ IN DOWNLOAD_CONTEXT *Context,=0D
+ IN OUT BOOLEAN *CallBackComplete=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ EFI_EVENT WaitEvt;=0D
+=0D
+ Status =3D EFI_SUCCESS;=0D
+=0D
+ // Use a timer to measure timeout. Cannot use Stall here!=0D
+ Status =3D gBS->CreateEvent (=0D
+ EVT_TIMER,=0D
+ TPL_CALLBACK,=0D
+ NULL,=0D
+ NULL,=0D
+ &WaitEvt=0D
+ );=0D
+ ASSERT_EFI_ERROR (Status);=0D
+=0D
+ if (!EFI_ERROR (Status)) {=0D
+ Status =3D gBS->SetTimer(=0D
+ WaitEvt,=0D
+ TimerRelative,=0D
+ EFI_TIMER_PERIOD_SECONDS (TIMER_MAX_TIMEOUT_S)=0D
+ );=0D
+=0D
+ ASSERT_EFI_ERROR (Status);=0D
+ }=0D
+=0D
+ while (! *CallBackComplete &&=0D
+ (!EFI_ERROR (Status)) &&=0D
+ EFI_ERROR (gBS->CheckEvent (WaitEvt))=0D
+ ) {=0D
+ Status =3D Context->Http->Poll (Context->Http);=0D
+ }=0D
+=0D
+ gBS->SetTimer (WaitEvt, TimerCancel, 0);=0D
+ gBS->CloseEvent (WaitEvt);=0D
+=0D
+ if (*CallBackComplete) {=0D
+ return EFI_SUCCESS;=0D
+ }=0D
+=0D
+ if (!EFI_ERROR (Status)) {=0D
+ Status =3D EFI_TIMEOUT;=0D
+ }=0D
+=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ Generate and send a request to the http server.=0D
+=0D
+ @param[in] Context HTTP download context.=0D
+ @param[in] DownloadUrl Fully qualified URL to be downloaded.=0D
+=0D
+ @return EFI_SUCCESS Request has been sent successfully.=0D
+ @return EFI_INVALID_PARAMETER Invalid URL.=0D
+ @return EFI_OUT_OF_RESOURCES Out of memory.=0D
+ @return EFI_DEVICE_ERROR If HTTPS is used, this probably=0D
+ means that TLS support either was not=0D
+ installed or not configured.=0D
+ @return Others Error sending the request.=0D
+**/=0D
+=0D
+STATIC=0D
+EFI_STATUS=0D
+SendRequest (=0D
+ IN DOWNLOAD_CONTEXT *Context,=0D
+ IN CHAR16 *DownloadUrl=0D
+ )=0D
+{=0D
+ EFI_HTTP_REQUEST_DATA RequestData;=0D
+ EFI_HTTP_HEADER RequestHeader;=0D
+ EFI_HTTP_MESSAGE RequestMessage;=0D
+ EFI_STATUS Status;=0D
+ CHAR16 *Host;=0D
+ UINTN StringSize;=0D
+=0D
+ ZeroMem (&RequestData, sizeof (RequestData));=0D
+ ZeroMem (&RequestHeader, sizeof (RequestHeader));=0D
+ ZeroMem (&RequestMessage, sizeof (RequestMessage));=0D
+ ZeroMem (&Context->RequestToken, sizeof (Context->RequestToken));=0D
+=0D
+ RequestHeader.FieldName =3D "Host";=0D
+=0D
+ Host =3D (CHAR16 *)Context->ServerAddrAndProto;=0D
+ while (*Host !=3D CHAR_NULL && *Host !=3D L'/') {=0D
+ Host++;=0D
+ }=0D
+=0D
+ if (*Host =3D=3D CHAR_NULL) {=0D
+ return EFI_INVALID_PARAMETER;=0D
+ }=0D
+=0D
+ //=0D
+ // Get the next slash=0D
+ //=0D
+ Host++;=0D
+ //=0D
+ // And now the host name=0D
+ //=0D
+ Host++;=0D
+=0D
+ StringSize =3D StrLen (Host) + 1;=0D
+ RequestHeader.FieldValue =3D AllocatePool (StringSize);=0D
+ if (!RequestHeader.FieldValue) {=0D
+ return EFI_OUT_OF_RESOURCES;=0D
+ }=0D
+=0D
+ UnicodeStrToAsciiStrS (Host, RequestHeader.FieldValue, StringSize);=0D
+=0D
+ RequestMessage.HeaderCount++;=0D
+=0D
+ RequestData.Method =3D HttpMethodGet;=0D
+ RequestData.Url =3D DownloadUrl;=0D
+=0D
+ RequestMessage.Data.Request =3D &RequestData;=0D
+ RequestMessage.Headers =3D &RequestHeader;=0D
+ RequestMessage.BodyLength =3D 0;=0D
+ RequestMessage.Body =3D NULL;=0D
+ Context->RequestToken.Event =3D NULL;=0D
+=0D
+ //=0D
+ // Completion callback event to be set when Request completes.=0D
+ //=0D
+ Status =3D gBS->CreateEvent (=0D
+ EVT_NOTIFY_SIGNAL,=0D
+ TPL_CALLBACK,=0D
+ RequestCallback,=0D
+ Context,=0D
+ &Context->RequestToken.Event=0D
+ );=0D
+ ASSERT_EFI_ERROR (Status);=0D
+=0D
+ Context->RequestToken.Status =3D EFI_SUCCESS;=0D
+ Context->RequestToken.Message =3D &RequestMessage;=0D
+ gRequestCallbackComplete =3D FALSE;=0D
+ Status =3D Context->Http->Request (Context->Http, &Context->RequestTok=
en);=0D
+ if (EFI_ERROR (Status)) {=0D
+ goto Error;=0D
+ }=0D
+=0D
+ Status =3D WaitForCompletion (Context, &gRequestCallbackComplete);=0D
+ if (EFI_ERROR (Status)) {=0D
+ Context->Http->Cancel (Context->Http, &Context->RequestToken);=0D
+ }=0D
+Error:=0D
+ SHELL_FREE_NON_NULL (RequestHeader.FieldValue);=0D
+ if (Context->RequestToken.Event) {=0D
+ gBS->CloseEvent (Context->RequestToken.Event);=0D
+ ZeroMem (&Context->RequestToken, sizeof (Context->RequestToken));=0D
+ }=0D
+=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ Update the progress of a file download=0D
+ This procedure is called each time a new HTTP body portion is received.=
=0D
+=0D
+ @param[in] Context HTTP download context.=0D
+ @param[in] DownloadLen Portion size, in bytes.=0D
+ @param[in] Buffer The pointer to the parsed buffer.=0D
+=0D
+ @retval EFI_SUCCESS Portion saved.=0D
+ @retval Other Error saving the portion.=0D
+=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+EFIAPI=0D
+SavePortion (=0D
+ IN DOWNLOAD_CONTEXT *Context,=0D
+ IN UINTN DownloadLen,=0D
+ IN CHAR8 *Buffer=0D
+ )=0D
+{=0D
+ CHAR16 Progress[HTTP_PROGRESS_MESSAGE_SIZE];=0D
+ UINTN NbOfKb;=0D
+ UINTN Index;=0D
+ UINTN LastStep;=0D
+ UINTN Step;=0D
+ EFI_STATUS Status;=0D
+=0D
+ LastStep =3D 0;=0D
+ Step =3D 0;=0D
+ ShellSetFilePosition (mFileHandle, Context->LastReportedNbOfBytes);=0D
+ Status =3D ShellWriteFile (mFileHandle, &DownloadLen, Buffer);=0D
+ if (EFI_ERROR (Status)) {=0D
+ if (Context->ContentDownloaded > 0) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_CRLF), mHttpHiiHandle=0D
+ );=0D
+ }=0D
+=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_WRITE),=0D
+ mHttpHiiHandle, mLocalFilePath, Status=0D
+ );=0D
+=0D
+ return Status;=0D
+ }=0D
+=0D
+ if (Context->ContentDownloaded =3D=3D 0) {=0D
+ ShellPrintEx (-1, -1, L"%s 0 Kb", HTTP_PROGR_FRAME);=0D
+ }=0D
+=0D
+ Context->ContentDownloaded +=3D DownloadLen;=0D
+ NbOfKb =3D Context->ContentDownloaded >> 10;=0D
+=0D
+ Progress[0] =3D L'\0';=0D
+ if (Context->ContentLength) {=0D
+ LastStep =3D (Context->LastReportedNbOfBytes * HTTP_PROGRESS_SLIDER_S=
TEPS) /=0D
+ Context->ContentLength;=0D
+ Step =3D (Context->ContentDownloaded * HTTP_PROGRESS_SLIDER_STEPS=
) /=0D
+ Context->ContentLength;=0D
+ }=0D
+=0D
+ Context->LastReportedNbOfBytes =3D Context->ContentDownloaded;=0D
+=0D
+ if (Step <=3D LastStep) {=0D
+ if (!Context->ContentLength) {=0D
+ //=0D
+ // Update downloaded size, there is no length info available.=0D
+ //=0D
+ ShellPrintEx (-1, -1, L"%s", HTTP_KB);=0D
+ ShellPrintEx (-1, -1, L"%7d Kb", NbOfKb);=0D
+ }=0D
+=0D
+ return EFI_SUCCESS;=0D
+ }=0D
+=0D
+ ShellPrintEx (-1, -1, L"%s", HTTP_PROGRESS_DEL);=0D
+=0D
+ Status =3D StrCpyS (Progress, HTTP_PROGRESS_MESSAGE_SIZE, HTTP_PROGR_FRA=
ME);=0D
+ if (EFI_ERROR (Status)) {=0D
+ return Status;=0D
+ }=0D
+=0D
+ for (Index =3D 1; Index < Step; Index++) {=0D
+ Progress[Index] =3D L'=3D';=0D
+ }=0D
+=0D
+ if (Step) {=0D
+ Progress[Step] =3D L'>';=0D
+ }=0D
+=0D
+ UnicodeSPrint (=0D
+ Progress + (sizeof (HTTP_PROGR_FRAME) / sizeof (CHAR16)) - 1,=0D
+ sizeof (Progress) - sizeof (HTTP_PROGR_FRAME),=0D
+ L" %7d Kb",=0D
+ NbOfKb=0D
+ );=0D
+=0D
+=0D
+ ShellPrintEx (-1, -1, L"%s", Progress);=0D
+=0D
+ return EFI_SUCCESS;=0D
+}=0D
+=0D
+/**=0D
+ Replace the original Host and URI with Host and URI returned by the=0D
+ HTTP server in 'Location' header (redirection).=0D
+=0D
+ @param[in] Location A pointer to the 'Location' string=0D
+ provided by HTTP server.=0D
+ @param[in] Context A pointer to HTTP download context.=0D
+ @param[in] DownloadUrl Fully qualified HTTP URL.=0D
+=0D
+ @return EFI_SUCCESS Host and URI were successfully set.=0D
+ @return EFI_OUT_OF_RESOURCES Error setting Host or URI.=0D
+**/=0D
+=0D
+STATIC=0D
+EFI_STATUS=0D
+SetHostURI (=0D
+ IN CHAR8 *Location,=0D
+ IN DOWNLOAD_CONTEXT *Context,=0D
+ IN CHAR16 *DownloadUrl=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ UINTN StringSize;=0D
+ UINTN FirstStep;=0D
+ UINTN Idx;=0D
+ UINTN Step;=0D
+ CHAR8 *Walker;=0D
+ CHAR16 *Temp;=0D
+ CHAR8 *Tmp;=0D
+ CHAR16 *Url;=0D
+ BOOLEAN IsAbEmptyUrl;=0D
+=0D
+ Tmp =3D NULL;=0D
+ Url =3D NULL;=0D
+ IsAbEmptyUrl =3D FALSE;=0D
+ FirstStep =3D 0;=0D
+=0D
+ StringSize =3D (AsciiStrSize (Location) * sizeof (CHAR16));=0D
+ Url =3D AllocateZeroPool (StringSize);=0D
+ if (!Url) {=0D
+ return EFI_OUT_OF_RESOURCES;=0D
+ }=0D
+=0D
+ Status =3D AsciiStrToUnicodeStrS (=0D
+ (CONST CHAR8 *)Location,=0D
+ Url,=0D
+ StringSize=0D
+ );=0D
+=0D
+ if (EFI_ERROR (Status)) {=0D
+ goto Error;=0D
+ }=0D
+=0D
+ //=0D
+ // If an HTTP server redirects to the same location more than once,=0D
+ // then stop attempts and tell it is not reachable.=0D
+ //=0D
+ if (!StrCmp (Url, DownloadUrl)) {=0D
+ Status =3D EFI_NO_MAPPING;=0D
+ goto Error;=0D
+ }=0D
+=0D
+ if (AsciiStrLen (Location) > 2) {=0D
+ // Some servers return 'Location: //server/resource'=0D
+ IsAbEmptyUrl =3D (Location[0] =3D=3D '/') && (Location[1] =3D=3D '/');=
=0D
+ if (IsAbEmptyUrl) {=0D
+ // Skip first "//"=0D
+ Location +=3D 2;=0D
+ FirstStep =3D 1;=0D
+ }=0D
+ }=0D
+=0D
+ if (AsciiStrStr (Location, "://") || IsAbEmptyUrl) {=0D
+ Idx =3D 0;=0D
+ Walker =3D Location;=0D
+=0D
+ for (Step =3D FirstStep; Step < 2; Step++) {=0D
+ for (; *Walker !=3D '/' && *Walker !=3D '\0'; Walker++) {=0D
+ Idx++;=0D
+ }=0D
+ if (!Step) {=0D
+ //=0D
+ // Skip "//"=0D
+ //=0D
+ Idx +=3D 2;=0D
+ Walker +=3D 2;=0D
+ }=0D
+ }=0D
+=0D
+ Tmp =3D AllocateZeroPool (Idx + 1);=0D
+ if (!Tmp) {=0D
+ Status =3D EFI_OUT_OF_RESOURCES;=0D
+ goto Error;=0D
+ }=0D
+=0D
+ CopyMem (Tmp, Location, Idx);=0D
+=0D
+ //=0D
+ // Location now points to URI=0D
+ //=0D
+ Location +=3D Idx;=0D
+ StringSize =3D (Idx + 1) * sizeof (CHAR16);=0D
+=0D
+ SHELL_FREE_NON_NULL (Context->ServerAddrAndProto);=0D
+=0D
+ Temp =3D AllocateZeroPool (StringSize);=0D
+ if (!Temp) {=0D
+ Status =3D EFI_OUT_OF_RESOURCES;=0D
+ goto Error;=0D
+ }=0D
+=0D
+ Status =3D AsciiStrToUnicodeStrS (=0D
+ (CONST CHAR8 *)Tmp,=0D
+ Temp,=0D
+ StringSize=0D
+ );=0D
+ if (EFI_ERROR (Status)) {=0D
+ SHELL_FREE_NON_NULL (Temp);=0D
+ goto Error;=0D
+ }=0D
+=0D
+ Idx =3D 0;=0D
+ if (IsAbEmptyUrl) {=0D
+ Context->ServerAddrAndProto =3D StrnCatGrow (=0D
+ &Context->ServerAddrAndProto,=0D
+ &Idx,=0D
+ L"http://",=0D
+ StrLen (L"http://")=0D
+ );=0D
+ }=0D
+=0D
+ Context->ServerAddrAndProto =3D StrnCatGrow (=0D
+ &Context->ServerAddrAndProto,=0D
+ &Idx,=0D
+ Temp,=0D
+ StrLen (Temp)=0D
+ );=0D
+ SHELL_FREE_NON_NULL (Temp);=0D
+ if (!Context->ServerAddrAndProto) {=0D
+ Status =3D EFI_OUT_OF_RESOURCES;=0D
+ goto Error;=0D
+ }=0D
+ }=0D
+=0D
+ SHELL_FREE_NON_NULL (Context->URI);=0D
+=0D
+ StringSize =3D AsciiStrSize (Location) * sizeof (CHAR16);=0D
+ Context->URI =3D AllocateZeroPool (StringSize);=0D
+ if (!Context->URI) {=0D
+ Status =3D EFI_OUT_OF_RESOURCES;=0D
+ goto Error;=0D
+ }=0D
+=0D
+ //=0D
+ // Now make changes to the URI part.=0D
+ //=0D
+ Status =3D AsciiStrToUnicodeStrS (=0D
+ (CONST CHAR8 *)Location,=0D
+ Context->URI,=0D
+ StringSize=0D
+ );=0D
+Error:=0D
+ SHELL_FREE_NON_NULL (Tmp);=0D
+ SHELL_FREE_NON_NULL (Url);=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ Message parser callback.=0D
+ Save a portion of HTTP body.=0D
+=0D
+ @param[in] EventType Type of event. Can be either=0D
+ OnComplete or OnData.=0D
+ @param[in] Data A pointer to the buffer with data.=0D
+ @param[in] Length Data length of this portion.=0D
+ @param[in] Context A pointer to the HTTP download context.=0D
+=0D
+ @return EFI_SUCCESS The portion was processed successfully.=0D
+ @return Other Error returned by SavePortion.=0D
+**/=0D
+=0D
+STATIC=0D
+EFI_STATUS=0D
+EFIAPI=0D
+ParseMsg (=0D
+ IN HTTP_BODY_PARSE_EVENT EventType,=0D
+ IN CHAR8 *Data,=0D
+ IN UINTN Length,=0D
+ IN VOID *Context=0D
+ )=0D
+{=0D
+ if (!Data || (EventType =3D=3D BodyParseEventOnComplete) || !Context) {=
=0D
+ return EFI_SUCCESS;=0D
+ }=0D
+=0D
+ return SavePortion (Context, Length, Data);=0D
+}=0D
+=0D
+=0D
+/**=0D
+ Get HTTP server response and collect the whole body as a file.=0D
+ Set appropriate status in Context (REQ_OK, REQ_REPEAT, REQ_ERROR).=0D
+ Note that even if HTTP server returns an error code, it might send=0D
+ the body as well. This body will be collected in the resultant file.=0D
+=0D
+ @param[in] Context A pointer to the HTTP download context.=0D
+ @param[in] DownloadedUrl A pointer to the fully qualified URL to dow=
nload.=0D
+=0D
+ @return EFI_SUCCESS Valid file. Body successfully collected.=0D
+ @return EFI_HTTP_ERROR Response is a valid HTTP response, but the=
=0D
+ HTTP server=0D
+ indicated an error (HTTP code >=3D 400).=0D
+ Response body MAY contain full=0D
+ HTTP server response.=0D
+ @return Others Error getting the reponse from the HTTP ser=
ver.=0D
+ Response body is not collected.=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+GetResponse (=0D
+ IN DOWNLOAD_CONTEXT *Context,=0D
+ IN CHAR16 *DownloadUrl=0D
+ )=0D
+{=0D
+ EFI_HTTP_RESPONSE_DATA ResponseData;=0D
+ EFI_HTTP_MESSAGE ResponseMessage;=0D
+ EFI_HTTP_HEADER *Header;=0D
+ EFI_STATUS Status;=0D
+ VOID *MsgParser;=0D
+ CONST CHAR16 *Desc;=0D
+ BOOLEAN IsTrunked;=0D
+ BOOLEAN Redirection;=0D
+=0D
+ ZeroMem (&ResponseData, sizeof (ResponseData));=0D
+ ZeroMem (&ResponseMessage, sizeof (ResponseMessage));=0D
+ ZeroMem (&Context->ResponseToken, sizeof (Context->ResponseToken));=0D
+ IsTrunked =3D FALSE;=0D
+=0D
+ ResponseMessage.Body =3D Context->Buffer;=0D
+ Context->ResponseToken.Status =3D EFI_SUCCESS;=0D
+ Context->ResponseToken.Message =3D &ResponseMessage;=0D
+ Context->ContentLength =3D 0;=0D
+ Context->Status =3D REQ_OK;=0D
+ MsgParser =3D NULL;=0D
+ ResponseData.StatusCode =3D HTTP_STATUS_UNSUPPORTED_STATUS;=0D
+ ResponseMessage.Data.Response =3D &ResponseData;=0D
+ Context->ResponseToken.Event =3D NULL;=0D
+=0D
+ do {=0D
+ SHELL_FREE_NON_NULL (ResponseMessage.Headers);=0D
+ ResponseMessage.HeaderCount =3D 0;=0D
+ gResponseCallbackComplete =3D FALSE;=0D
+ ResponseMessage.BodyLength =3D Context->BufferSize;=0D
+=0D
+ if (ShellGetExecutionBreakFlag ()) {=0D
+ Status =3D EFI_ABORTED;=0D
+ break;=0D
+ }=0D
+=0D
+ if (!Context->ContentDownloaded && !Context->ResponseToken.Event) {=0D
+ Status =3D gBS->CreateEvent (=0D
+ EVT_NOTIFY_SIGNAL,=0D
+ TPL_CALLBACK,=0D
+ ResponseCallback,=0D
+ Context,=0D
+ &Context->ResponseToken.Event=0D
+ );=0D
+ ASSERT_EFI_ERROR (Status);=0D
+ } else {=0D
+ ResponseMessage.Data.Response =3D NULL;=0D
+ }=0D
+=0D
+ if (EFI_ERROR (Status)) {=0D
+ break;=0D
+ }=0D
+=0D
+ Status =3D Context->Http->Response (Context->Http, &Context->ResponseT=
oken);=0D
+ if (EFI_ERROR (Status)) {=0D
+ break;=0D
+ }=0D
+=0D
+ Status =3D WaitForCompletion (Context, &gResponseCallbackComplete);=0D
+ if (EFI_ERROR (Status) && ResponseMessage.HeaderCount) {=0D
+ Status =3D EFI_SUCCESS;=0D
+ }=0D
+=0D
+ if (EFI_ERROR (Status)) {=0D
+ Context->Http->Cancel (Context->Http, &Context->ResponseToken);=0D
+ break;=0D
+ }=0D
+=0D
+ if (!Context->ContentDownloaded) {=0D
+ Redirection =3D=0D
+ ((ResponseData.StatusCode >=3D HTTP_STATUS_300_MULTIPLE_CHOICES) &=
&=0D
+ (ResponseData.StatusCode <=3D HTTP_STATUS_307_TEMPORARY_REDIRECT)=
=0D
+ ) ||=0D
+ (ResponseData.StatusCode =3D=3D HTTP_STATUS_308_PERMANENT_REDIRECT=
);=0D
+=0D
+ if (Redirection) {=0D
+ //=0D
+ // Need to repeat the request with new Location (server redirected=
).=0D
+ //=0D
+ Context->Status =3D REQ_NEED_REPEAT;=0D
+=0D
+ Header =3D HttpFindHeader (=0D
+ ResponseMessage.HeaderCount,=0D
+ ResponseMessage.Headers,=0D
+ "Location"=0D
+ );=0D
+ if (Header) {=0D
+ Status =3D SetHostURI (Header->FieldValue, Context, DownloadUrl)=
;=0D
+ if (Status =3D=3D EFI_NO_MAPPING) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_STATUSCODE),=0D
+ mHttpHiiHandle, Context->ServerAddrAndProto,=0D
+ L"Recursive HTTP server relocation",=0D
+ Context->URI=0D
+ );=0D
+ }=0D
+ } else {=0D
+ //=0D
+ // Bad reply from the server. Server must specify the location.=
=0D
+ // Indicate that resource was not found, and no body collected.=
=0D
+ //=0D
+ Status =3D EFI_NOT_FOUND;=0D
+ }=0D
+=0D
+ Context->Http->Cancel (Context->Http, &Context->ResponseToken);=0D
+ break;=0D
+ }=0D
+=0D
+ //=0D
+ // Init message-body parser by header information.=0D
+ //=0D
+ if (!MsgParser) {=0D
+ Status =3D HttpInitMsgParser (=0D
+ ResponseMessage.Data.Request->Method,=0D
+ ResponseData.StatusCode,=0D
+ ResponseMessage.HeaderCount,=0D
+ ResponseMessage.Headers,=0D
+ ParseMsg,=0D
+ Context,=0D
+ &MsgParser=0D
+ );=0D
+ if (EFI_ERROR (Status)) {=0D
+ break;=0D
+ }=0D
+ }=0D
+=0D
+ //=0D
+ // If it is a trunked message, rely on the parser.=0D
+ //=0D
+ Header =3D HttpFindHeader (=0D
+ ResponseMessage.HeaderCount,=0D
+ ResponseMessage.Headers,=0D
+ "Transfer-Encoding"=0D
+ );=0D
+ IsTrunked =3D (Header && !AsciiStrCmp (Header->FieldValue, "chunked"=
));=0D
+=0D
+ HttpGetEntityLength (MsgParser, &Context->ContentLength);=0D
+=0D
+ if (ResponseData.StatusCode >=3D HTTP_STATUS_400_BAD_REQUEST &&=0D
+ (ResponseData.StatusCode !=3D HTTP_STATUS_308_PERMANENT_REDIRECT=
)=0D
+ ) {=0D
+ //=0D
+ // Server reported an error via Response code.=0D
+ // Collect the body if any.=0D
+ //=0D
+ if (!gHttpError) {=0D
+ gHttpError =3D TRUE;=0D
+=0D
+ Desc =3D ErrStatusDesc[ResponseData.StatusCode -=0D
+ HTTP_STATUS_400_BAD_REQUEST];=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_ERR_STATUSCODE),=0D
+ mHttpHiiHandle, Context->ServerAddrAndProto,=0D
+ Desc,=0D
+ Context->URI=0D
+ );=0D
+=0D
+ //=0D
+ // This gives an RFC HTTP error.=0D
+ //=0D
+ Context->Status =3D ShellStrToUintn (Desc);=0D
+ }=0D
+ }=0D
+ }=0D
+=0D
+ // Do NOT try to parse an empty body.=0D
+ if (ResponseMessage.BodyLength || IsTrunked) {=0D
+ Status =3D HttpParseMessageBody (=0D
+ MsgParser,=0D
+ ResponseMessage.BodyLength,=0D
+ ResponseMessage.Body=0D
+ );=0D
+ }=0D
+ } while (!HttpIsMessageComplete (MsgParser) &&=0D
+ !EFI_ERROR (Status) &&=0D
+ ResponseMessage.BodyLength=0D
+ );=0D
+=0D
+ SHELL_FREE_NON_NULL (MsgParser);=0D
+ if (Context->ResponseToken.Event) {=0D
+ gBS->CloseEvent (Context->ResponseToken.Event);=0D
+ ZeroMem (&Context->ResponseToken, sizeof (Context->ResponseToken));=0D
+ }=0D
+=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ Worker function that downloads the data of a file from an HTTP server gi=
ven=0D
+ the path of the file and its size.=0D
+=0D
+ @param[in] Context A pointer to the HTTP download context.=0D
+=0D
+ @retval EFI_SUCCESS The file was downloaded.=0D
+ @retval EFI_OUT_OF_RESOURCES A memory allocation failed.=0D
+ #retval EFI_HTTP_ERROR The server returned a valid HTTP error.=0D
+ Examine the mLocalFilePath file=0D
+ to get error body.=0D
+ @retval Others The downloading of the file from the serv=
er=0D
+ failed.=0D
+=0D
+**/=0D
+STATIC=0D
+EFI_STATUS=0D
+DownloadFile (=0D
+ IN DOWNLOAD_CONTEXT *Context=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ CHAR16 *DownloadUrl;=0D
+ UINTN UrlSize;=0D
+=0D
+ ASSERT (Context);=0D
+ if (!Context) {=0D
+ return EFI_INVALID_PARAMETER;=0D
+ }=0D
+=0D
+ DownloadUrl =3D NULL;=0D
+=0D
+ Context->Buffer =3D AllocatePool (Context->BufferSize);=0D
+ if (!Context->Buffer) {=0D
+ Status =3D EFI_OUT_OF_RESOURCES;=0D
+ goto ON_EXIT;=0D
+ }=0D
+=0D
+ //=0D
+ // OPEN FILE=0D
+ //=0D
+ if (!EFI_ERROR (ShellFileExists (mLocalFilePath))) {=0D
+ ShellDeleteFileByName (mLocalFilePath);=0D
+ }=0D
+=0D
+ Status =3D ShellOpenFileByName (=0D
+ mLocalFilePath,=0D
+ &mFileHandle,=0D
+ EFI_FILE_MODE_CREATE |=0D
+ EFI_FILE_MODE_WRITE |=0D
+ EFI_FILE_MODE_READ,=0D
+ 0);=0D
+ if (EFI_ERROR (Status)) {=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),=0D
+ mHttpHiiHandle, HTTP_APP_NAME, mLocalFilePath=0D
+ );=0D
+ goto ON_EXIT;=0D
+ }=0D
+=0D
+ do {=0D
+ SHELL_FREE_NON_NULL (DownloadUrl);=0D
+ UrlSize =3D 0;=0D
+ DownloadUrl =3D StrnCatGrow (=0D
+ &DownloadUrl,=0D
+ &UrlSize,=0D
+ Context->ServerAddrAndProto,=0D
+ StrLen (Context->ServerAddrAndProto)=0D
+ );=0D
+ if (Context->URI[0] !=3D L'/') {=0D
+ DownloadUrl =3D StrnCatGrow (=0D
+ &DownloadUrl,=0D
+ &UrlSize,=0D
+ L"/",=0D
+ StrLen (Context->ServerAddrAndProto)=0D
+ );=0D
+ }=0D
+=0D
+ DownloadUrl =3D StrnCatGrow (=0D
+ &DownloadUrl,=0D
+ &UrlSize,=0D
+ Context->URI,=0D
+ StrLen (Context->URI));=0D
+=0D
+ ShellPrintHiiEx (=0D
+ -1, -1, NULL, STRING_TOKEN (STR_HTTP_DOWNLOADING),=0D
+ mHttpHiiHandle, DownloadUrl);=0D
+=0D
+ Status =3D SendRequest (Context, DownloadUrl);=0D
+ if (Status) {=0D
+ goto ON_EXIT;=0D
+ }=0D
+=0D
+ Status =3D GetResponse (Context, DownloadUrl);=0D
+=0D
+ if (Status) {=0D
+ goto ON_EXIT;=0D
+ }=0D
+=0D
+ } while (Context->Status =3D=3D REQ_NEED_REPEAT);=0D
+=0D
+ if (Context->Status) {=0D
+ Status =3D ENCODE_ERROR (Context->Status);=0D
+ }=0D
+=0D
+ON_EXIT:=0D
+ //=0D
+ // CLOSE FILE=0D
+ //=0D
+ if (mFileHandle) {=0D
+ if (EFI_ERROR (Status) && !(Context->Flags & DL_FLAG_KEEP_BAD)) {=0D
+ ShellDeleteFile (&mFileHandle);=0D
+ } else {=0D
+ ShellCloseFile (&mFileHandle);=0D
+ }=0D
+ }=0D
+=0D
+ SHELL_FREE_NON_NULL (DownloadUrl);=0D
+ SHELL_FREE_NON_NULL (Context->Buffer);=0D
+=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ Retrive HII package list from ImageHandle and publish to HII database.=0D
+=0D
+ @param ImageHandle The image handle of the process.=0D
+=0D
+ @return HII handle.=0D
+**/=0D
+EFI_HII_HANDLE=0D
+InitializeHiiPackage (=0D
+ EFI_HANDLE ImageHandle=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;=0D
+ EFI_HII_HANDLE HiiHandle;=0D
+=0D
+ //=0D
+ // Retrieve HII package list from ImageHandle=0D
+ //=0D
+ Status =3D gBS->OpenProtocol (=0D
+ ImageHandle,=0D
+ &gEfiHiiPackageListProtocolGuid,=0D
+ (VOID **)&PackageList,=0D
+ ImageHandle,=0D
+ NULL,=0D
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL=0D
+ );=0D
+ ASSERT_EFI_ERROR (Status);=0D
+ if (EFI_ERROR (Status)) {=0D
+ return NULL;=0D
+ }=0D
+=0D
+ //=0D
+ // Publish HII package list to HII Database.=0D
+ //=0D
+ Status =3D gHiiDatabase->NewPackageList (=0D
+ gHiiDatabase,=0D
+ PackageList,=0D
+ NULL,=0D
+ &HiiHandle=0D
+ );=0D
+ ASSERT_EFI_ERROR (Status);=0D
+ if (EFI_ERROR (Status)) {=0D
+ return NULL;=0D
+ }=0D
+=0D
+ return HiiHandle;=0D
+}=0D
diff --git a/ShellPkg/DynamicCommand/HttpDynamicCommand/Http.h b/ShellPkg/D=
ynamicCommand/HttpDynamicCommand/Http.h
new file mode 100644
index 000000000000..5c7b90e8cab4
--- /dev/null
+++ b/ShellPkg/DynamicCommand/HttpDynamicCommand/Http.h
@@ -0,0 +1,89 @@
+/** @file=0D
+ Header file for 'http' command functions.=0D
+=0D
+ Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved. <BR>=
=0D
+ Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>=0D
+ Copyright (c) 2020, Broadcom. All rights reserved.<BR>=0D
+=0D
+ SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+=0D
+#ifndef _HTTP_H_=0D
+#define _HTTP_H_=0D
+=0D
+#include <Uefi.h>=0D
+=0D
+#include <Library/BaseLib.h>=0D
+#include <Library/BaseMemoryLib.h>=0D
+#include <Library/DebugLib.h>=0D
+#include <Library/HiiLib.h>=0D
+#include <Library/HttpLib.h>=0D
+#include <Library/MemoryAllocationLib.h>=0D
+#include <Library/NetLib.h>=0D
+#include <Library/PrintLib.h>=0D
+#include <Library/ShellLib.h>=0D
+#include <Library/UefiBootServicesTableLib.h>=0D
+#include <Library/UefiHiiServicesLib.h>=0D
+#include <Library/UefiLib.h>=0D
+#include <Library/UefiRuntimeServicesTableLib.h>=0D
+=0D
+#include <Protocol/HiiPackageList.h>=0D
+#include <Protocol/HttpUtilities.h>=0D
+#include <Protocol/ServiceBinding.h>=0D
+=0D
+#define HTTP_APP_NAME L"http"=0D
+=0D
+#define REQ_OK 0=0D
+#define REQ_NEED_REPEAT 1=0D
+=0D
+// Download Flags=0D
+#define DL_FLAG_TIME BIT0 // Show elapsed time.=0D
+#define DL_FLAG_KEEP_BAD BIT1 // Keep files even if download failed.=0D
+=0D
+extern EFI_HII_HANDLE mHttpHiiHandle;=0D
+=0D
+typedef struct {=0D
+ UINTN ContentDownloaded;=0D
+ UINTN ContentLength;=0D
+ UINTN LastReportedNbOfBytes;=0D
+ UINTN BufferSize;=0D
+ UINTN Status;=0D
+ UINTN Flags;=0D
+ UINT8 *Buffer;=0D
+ CHAR16 *ServerAddrAndProto;=0D
+ CHAR16 *URI;=0D
+ EFI_HTTP_TOKEN ResponseToken;=0D
+ EFI_HTTP_TOKEN RequestToken;=0D
+ EFI_HTTP_PROTOCOL *Http;=0D
+} DOWNLOAD_CONTEXT;=0D
+=0D
+/**=0D
+ Function for 'http' command.=0D
+=0D
+ @param[in] ImageHandle The image handle.=0D
+ @param[in] SystemTable The system table.=0D
+=0D
+ @retval SHELL_SUCCESS Command completed successfully.=0D
+ @retval SHELL_INVALID_PARAMETER Command usage error.=0D
+ @retval SHELL_ABORTED The user aborts the operation.=0D
+ @retval value Unknown error.=0D
+**/=0D
+SHELL_STATUS=0D
+RunHttp (=0D
+ IN EFI_HANDLE ImageHandle,=0D
+ IN EFI_SYSTEM_TABLE *SystemTable=0D
+ );=0D
+=0D
+/**=0D
+ Retrive HII package list from ImageHandle and publish to HII database.=0D
+=0D
+ @param ImageHandle The image handle of the process.=0D
+=0D
+ @return HII handle.=0D
+**/=0D
+EFI_HII_HANDLE=0D
+InitializeHiiPackage (=0D
+ EFI_HANDLE ImageHandle=0D
+ );=0D
+#endif // _HTTP_H_=0D
diff --git a/ShellPkg/DynamicCommand/HttpDynamicCommand/Http.uni b/ShellPkg=
/DynamicCommand/HttpDynamicCommand/Http.uni
new file mode 100644
index 000000000000..00cf05deeb5c
--- /dev/null
+++ b/ShellPkg/DynamicCommand/HttpDynamicCommand/Http.uni
@@ -0,0 +1,117 @@
+// /**=0D
+//=0D
+// (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>=0D
+// Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved. <BR>=
=0D
+// Copyright (c) 2020, Broadcom. All rights reserved.<BR>=0D
+// SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+//=0D
+// Module Name:=0D
+//=0D
+// Http.uni=0D
+//=0D
+// Abstract:=0D
+//=0D
+// String definitions for UEFI Shell HTTP command=0D
+//=0D
+//=0D
+// **/=0D
+=0D
+/=3D#=0D
+=0D
+#langdef en-US "english"=0D
+=0D
+#string STR_GEN_TOO_MANY #language en-US "%H%s%N: Too many argum=
ents. Try help http.\r\n"=0D
+#string STR_GEN_TOO_FEW #language en-US "%H%s%N: Too few argume=
nts. Try help http.\r\n"=0D
+#string STR_GEN_PARAM_INV #language en-US "%H%s%N: Invalid argume=
nt - '%H%s%N'. Try help http.\r\n"=0D
+#string STR_GEN_PROBLEM #language en-US "%H%s%N: Unknown flag -=
'%H%s%N'. Try help http.\r\n"=0D
+#string STR_GEN_FILE_OPEN_FAIL #language en-US "%H%s%N: Cannot open fi=
le - '%H%s%N'\r\n"=0D
+#string STR_GEN_CRLF #language en-US "\r\n"=0D
+=0D
+#string STR_HTTP_ERR_NO_NIC #language en-US "No network interface c=
ard found.\r\n"=0D
+#string STR_HTTP_ERR_NIC_NAME #language en-US "Failed to get the name=
of the network interface card number %d - %r\r\n"=0D
+#string STR_HTTP_ERR_OPEN_PROTOCOL #language en-US "Unable to open HTTP pr=
otocol on '%H%s%N' - %r\r\n"=0D
+#string STR_HTTP_ERR_CONFIGURE #language en-US "Unable to configure HT=
TP protocol on '%H%s%N' - %r\r\n"=0D
+#string STR_HTTP_ERR_DOWNLOAD #language en-US "Unable to download the=
file '%H%s%N' on '%H%s%N' - %r\r\n"=0D
+#string STR_HTTP_ERR_WRITE #language en-US "Unable to write into f=
ile '%H%s%N' - %r\r\n"=0D
+#string STR_HTTP_ERR_NIC_NOT_FOUND #language en-US "Network Interface Card=
'%H%s%N' not found.\r\n"=0D
+#string STR_HTTP_ERR_STATUSCODE #language en-US "\r'%H%s%N' reports '%s=
' for '%H%s%N' \r\n"=0D
+#string STR_HTTP_DOWNLOADING #language en-US "Downloading '%H%s%N'\r=
\n"=0D
+=0D
+#string STR_GET_HELP_HTTP #language en-US ""=0D
+".TH http 0 "Download a file from HTTP server."\r\n"=0D
+".SH NAME\r\n"=0D
+"Download a file from HTTP server.\r\n"=0D
+".SH SYNOPSIS\r\n"=0D
+" \r\n"=0D
+"HTTP [-i interface] [-l port] [-t timeout] [-s size] [-m] [-k]\r\n"=0D
+" <URL> [localfilepath]\r\n"=0D
+".SH OPTIONS\r\n"=0D
+" \r\n"=0D
+" -i interface - Specifies an adapter name, i.e., eth0.\r\n"=0D
+" -k Keep the downloaded file even if there was an error.=
\r\n"=0D
+" If this parameter is not used, the file will be dele=
ted.\r\n"=0D
+" -l port - Specifies the local port number. Default value is 0\=
r\n"=0D
+" and the port number is automatically assigned.\r\n"=
=0D
+" -m Measure and report download time (in seconds). \r\n"=
=0D
+" -s size The size of the download buffer for a chunk, in byte=
s.\r\n"=0D
+" Default is 32K. Note that larger buffer does not imp=
ly\r\n"=0D
+" better speed.\r\n"=0D
+" -t timeout - The number of seconds to wait for completion of\r\n"=
=0D
+" requests and responses. Default is 0 which is 'autom=
atic'.\r\n"=0D
+" %HURL%N\r\n"=0D
+" Two types of providing of URLs are supported:\r\n"=0D
+" 1. tftp-like, where host and http_uri are separate parameters\r\n"=0D
+" (example: host /host_uri), and\r\n\"=0D
+" 2. wget-like, where host and host_uri is one parameter.\r\n"=0D
+" (example: host/host_uri)\r\n"=0D
+"\r\n"=0D
+" host - Specifies HTTP Server address.\r\n=0D
+ Can be either IPv4 address or 'http (or https)://a=
ddr'\r\n=0D
+ Can use addresses resolvable by DNS as well. \r\n=
=0D
+ Port can be specified after ':' if needed. \r\n=0D
+ By default port 80 is used.\r\n"=0D
+" http_uri - HTTP server URI to download the file.\r\n"=0D
+"\r\n"=0D
+" localfilepath - Local destination file path.\r\n"=0D
+".SH DESCRIPTION\r\n"=0D
+" \r\n"=0D
+"NOTES:\r\n"=0D
+" 1. The HTTP command allows geting of the file specified by its 'http_ur=
i'\r\n"=0D
+" path from the HTTP server specified by its 'host' IPv4 address. If t=
he\r\n"=0D
+" optional 'localfilepath' parameter is provided, the downloaded file =
is\r\n"=0D
+" stored locally using the provided file path. If the local file path =
is\r\n"=0D
+" not specified, the file is stored in the current directory using the=
file\r\n"=0D
+" server's name.\r\n"=0D
+" 2. Before using the HTTP command, the network interface intended to be\=
r\n"=0D
+" used to retrieve the file must be configured. This configuration may=
be\r\n"=0D
+" done by means of the 'ifconfig' command.\r\n"=0D
+" 3. If a network interface is defined with the '-i' option then only thi=
s\r\n"=0D
+" interface will be used to retrieve the remote file. Otherwise, all n=
etwork\r\n"=0D
+" interfaces are tried in the order they have been discovered during t=
he\r\n"=0D
+" DXE phase.\r\n"=0D
+".SH EXAMPLES\r\n"=0D
+" \r\n"=0D
+"EXAMPLES:\r\n"=0D
+" * To get the file "dir1/file1.dat" from the HTTP server 192.168.1.1, po=
rt 8080, and\r\n"=0D
+" store it as file2.dat in the current directory (use tftp-like URL for=
mat) :\r\n"=0D
+" fs0:\> http 192.168.1.1:8080 dir1/file1.dat file2.dat\r\n"=0D
+" * To get the file /image.bin via HTTPS from server 192.168.1.1 at port =
443 \r\n"=0D
+" (default HTTPS port), and store it in the current directory: \r\n"=0D
+" fs0:\> http https://192.168.1.1 image.bin\r\n"=0D
+" To get an index file from http://google.com and place it into the \r\=
n"=0D
+" current directory:\r\n"=0D
+" fs0:\> http google.com index.html\r\n"=0D
+".SH RETURNVALUES\r\n"=0D
+" \r\n"=0D
+"RETURN VALUES:\r\n"=0D
+" SHELL_SUCCESS The action was completed as requested.\r\n"=0D
+" SHELL_INVALID_PARAMETER One of the passed-in parameters was incorrect=
ly\r\n"=0D
+" formatted or its value was out of bounds.\r\n=
"=0D
+" HTTP_ERROR No EFI errors, but the server reported a stat=
us code\r\n"=0D
+" which should be treated as an error. If an er=
ror body sent\r\n"=0D
+" by the server, and -k parameter is on command=
line,=0D
+" the file wil be saved either as localfilepath=
filename,\r\n"=0D
+" or as an URI name in the current directory.\r=
\n"=0D
+" If '/' is at the end of the URL, and no locaf=
ilepath filename\r\n"=0D
+" is given on the command line, the file will b=
e retrieved as\r\n"=0D
+" index.html.\r\n"=0D
diff --git a/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.c b/ShellPk=
g/DynamicCommand/HttpDynamicCommand/HttpApp.c
new file mode 100644
index 000000000000..7bd5b46d3997
--- /dev/null
+++ b/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.c
@@ -0,0 +1,53 @@
+/** @file=0D
+ Entrypoint of "http" shell standalone application.=0D
+=0D
+ Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved. <BR>=
=0D
+ Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>=0D
+ Copyright (c) 2020, Broadcom. All rights reserved.<BR>=0D
+=0D
+ SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+#include "Http.h"=0D
+=0D
+/*=0D
+ * String token ID of help message text.=0D
+ * Shell supports to find help message in the resource section of an=0D
+ * application image if * .MAN file is not found.=0D
+ * This global variable is added to make build tool recognizes=0D
+ * that the help string is consumed by user and then build tool will=0D
+ * add the string into the resource section.=0D
+ * Thus the application can use '-?' option to show help message in Shell.=
=0D
+ */=0D
+GLOBAL_REMOVE_IF_UNREFERENCED=0D
+EFI_STRING_ID mStringHelpTokenId =3D STRING_TOKEN (STR_GET_HELP_HTTP);=0D
+=0D
+/**=0D
+ Entry point of Http standalone application.=0D
+=0D
+ @param ImageHandle The image handle of the process.=0D
+ @param SystemTable The EFI System Table pointer.=0D
+=0D
+ @retval EFI_SUCCESS Http command is executed sucessfully.=0D
+ @retval EFI_ABORTED HII package was failed to initialize.=0D
+ @retval others Other errors when executing http command.=
=0D
+**/=0D
+EFI_STATUS=0D
+EFIAPI=0D
+HttpAppInitialize (=0D
+ IN EFI_HANDLE ImageHandle,=0D
+ IN EFI_SYSTEM_TABLE *SystemTable=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+ mHttpHiiHandle =3D InitializeHiiPackage (ImageHandle);=0D
+ if (mHttpHiiHandle =3D=3D NULL) {=0D
+ return EFI_ABORTED;=0D
+ }=0D
+=0D
+ Status =3D (EFI_STATUS)RunHttp (ImageHandle, SystemTable);=0D
+=0D
+ HiiRemovePackages (mHttpHiiHandle);=0D
+=0D
+ return Status;=0D
+}=0D
diff --git a/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.inf b/Shell=
Pkg/DynamicCommand/HttpDynamicCommand/HttpApp.inf
new file mode 100644
index 000000000000..d08d47fb37d5
--- /dev/null
+++ b/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.inf
@@ -0,0 +1,58 @@
+## @file=0D
+# Provides Shell 'http' standalone application.=0D
+#=0D
+# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved. <BR>=
=0D
+# Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>=0D
+# Copyright (c) 2020, Broadcom. All rights reserved.<BR>=0D
+#=0D
+# SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+#=0D
+#=0D
+##=0D
+=0D
+[Defines]=0D
+ INF_VERSION =3D 0x00010006=0D
+ BASE_NAME =3D http=0D
+ FILE_GUID =3D 56B00FB7-91D2-869B-CE5C-26CD1A89C73C=
=0D
+ MODULE_TYPE =3D UEFI_APPLICATION=0D
+ VERSION_STRING =3D 1.0=0D
+ ENTRY_POINT =3D HttpAppInitialize=0D
+#=0D
+# This flag specifies whether HII resource section is generated into PE i=
mage.=0D
+#=0D
+ UEFI_HII_RESOURCE_SECTION =3D TRUE=0D
+=0D
+[Sources.common]=0D
+ Http.c=0D
+ HttpApp.c=0D
+ Http.h=0D
+ Http.uni=0D
+=0D
+[Packages]=0D
+ EmbeddedPkg/EmbeddedPkg.dec=0D
+ MdeModulePkg/MdeModulePkg.dec=0D
+ MdePkg/MdePkg.dec=0D
+ NetworkPkg/NetworkPkg.dec=0D
+ ShellPkg/ShellPkg.dec=0D
+=0D
+[LibraryClasses]=0D
+ BaseLib=0D
+ BaseMemoryLib=0D
+ DebugLib=0D
+ FileHandleLib=0D
+ HiiLib=0D
+ HttpLib=0D
+ MemoryAllocationLib=0D
+ NetLib=0D
+ ShellLib=0D
+ UefiApplicationEntryPoint=0D
+ UefiBootServicesTableLib=0D
+ UefiHiiServicesLib=0D
+ UefiLib=0D
+ UefiRuntimeServicesTableLib=0D
+=0D
+[Protocols]=0D
+ gEfiHiiPackageListProtocolGuid ## CONSUMES=0D
+ gEfiHttpProtocolGuid ## CONSUMES=0D
+ gEfiHttpServiceBindingProtocolGuid ## CONSUMES=0D
+ gEfiManagedNetworkServiceBindingProtocolGuid ## CONSUMES=0D
diff --git a/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.=
c b/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.c
new file mode 100644
index 000000000000..ba654749a075
--- /dev/null
+++ b/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.c
@@ -0,0 +1,134 @@
+/** @file=0D
+ Produce "http" shell dynamic command.=0D
+=0D
+ Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved. <BR>=
=0D
+ Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>=0D
+ Copyright (c) 2020, Broadcom. All rights reserved.<BR>=0D
+=0D
+ SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+=0D
+**/=0D
+#include <Protocol/ShellDynamicCommand.h>=0D
+#include "Http.h"=0D
+=0D
+/**=0D
+ This is the shell command handler function pointer callback type. This=
=0D
+ function handles the command when it is invoked in the shell.=0D
+=0D
+ @param[in] This The instance of the=0D
+ EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.=0D
+ @param[in] SystemTable The pointer to the system table.=0D
+ @param[in] ShellParameters The parameters associated with the command=
.=0D
+ @param[in] Shell The instance of the shell protocol used in=
=0D
+ the context of processing this command.=0D
+=0D
+ @return EFI_SUCCESS the operation was sucessful=0D
+ @return other the operation failed.=0D
+**/=0D
+SHELL_STATUS=0D
+EFIAPI=0D
+HttpCommandHandler (=0D
+ IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,=0D
+ IN EFI_SYSTEM_TABLE *SystemTable,=0D
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,=0D
+ IN EFI_SHELL_PROTOCOL *Shell=0D
+ )=0D
+{=0D
+ gEfiShellParametersProtocol =3D ShellParameters;=0D
+ gEfiShellProtocol =3D Shell;=0D
+=0D
+ return RunHttp (gImageHandle, SystemTable);=0D
+}=0D
+=0D
+/**=0D
+ This is the command help handler function pointer callback type. This=0D
+ function is responsible for displaying help information for the associat=
ed=0D
+ command.=0D
+=0D
+ @param[in] This The instance of the EFI_SHELL_DYNAMIC_COMMAND_PRO=
TOCOL.=0D
+ @param[in] Language The pointer to the language string to use.=0D
+=0D
+ @return string Pool allocated help string, must be freed by call=
er=0D
+**/=0D
+CHAR16 *=0D
+EFIAPI=0D
+HttpCommandGetHelp (=0D
+ IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,=0D
+ IN CONST CHAR8 *Language=0D
+ )=0D
+{=0D
+ return HiiGetString(mHttpHiiHandle,=0D
+ STRING_TOKEN (STR_GET_HELP_HTTP),=0D
+ Language);=0D
+}=0D
+=0D
+EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mHttpDynamicCommand =3D {=0D
+ HTTP_APP_NAME,=0D
+ HttpCommandHandler,=0D
+ HttpCommandGetHelp=0D
+};=0D
+=0D
+/**=0D
+ Entry point of Http Dynamic Command.=0D
+=0D
+ Produce the DynamicCommand protocol to handle "http" command.=0D
+=0D
+ @param ImageHandle The image handle of the process.=0D
+ @param SystemTable The EFI System Table pointer.=0D
+=0D
+ @retval EFI_SUCCESS Http command is executed sucessfully.=0D
+ @retval EFI_ABORTED HII package was failed to initialize.=0D
+ @retval others Other errors when executing http command.=
=0D
+**/=0D
+EFI_STATUS=0D
+EFIAPI=0D
+HttpCommandInitialize (=0D
+ IN EFI_HANDLE ImageHandle,=0D
+ IN EFI_SYSTEM_TABLE *SystemTable=0D
+ )=0D
+{=0D
+ EFI_STATUS Status;=0D
+=0D
+ mHttpHiiHandle =3D InitializeHiiPackage (ImageHandle);=0D
+ if (mHttpHiiHandle =3D=3D NULL) {=0D
+ return EFI_ABORTED;=0D
+ }=0D
+=0D
+ Status =3D gBS->InstallProtocolInterface (=0D
+ &ImageHandle,=0D
+ &gEfiShellDynamicCommandProtocolGuid,=0D
+ EFI_NATIVE_INTERFACE,=0D
+ &mHttpDynamicCommand=0D
+ );=0D
+ ASSERT_EFI_ERROR (Status);=0D
+ return Status;=0D
+}=0D
+=0D
+/**=0D
+ Http driver unload handler.=0D
+=0D
+ @param ImageHandle The image handle of the process.=0D
+=0D
+ @retval EFI_SUCCESS The image is unloaded.=0D
+ @retval Others Failed to unload the image.=0D
+**/=0D
+EFI_STATUS=0D
+EFIAPI=0D
+HttpUnload (=0D
+ IN EFI_HANDLE ImageHandle=0D
+)=0D
+{=0D
+ EFI_STATUS Status;=0D
+ Status =3D gBS->UninstallProtocolInterface (=0D
+ ImageHandle,=0D
+ &gEfiShellDynamicCommandProtocolGuid,=0D
+ &mHttpDynamicCommand=0D
+ );=0D
+ if (EFI_ERROR (Status)) {=0D
+ return Status;=0D
+ }=0D
+=0D
+ HiiRemovePackages (mHttpHiiHandle);=0D
+=0D
+ return EFI_SUCCESS;=0D
+}=0D
diff --git a/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.=
inf b/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
new file mode 100644
index 000000000000..5d46ee2384d5
--- /dev/null
+++ b/ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf
@@ -0,0 +1,63 @@
+## @file=0D
+# Provides Shell 'http' dynamic command.=0D
+#=0D
+# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved. <BR>=
=0D
+# Copyright (c) 2015, ARM Ltd. All rights reserved.<BR>=0D
+# Copyright (c) 2020, Broadcom. All rights reserved.<BR>=0D
+#=0D
+# SPDX-License-Identifier: BSD-2-Clause-Patent=0D
+#=0D
+#=0D
+##=0D
+=0D
+[Defines]=0D
+ INF_VERSION =3D 0x00010006=0D
+ BASE_NAME =3D httpDynamicCommand=0D
+ FILE_GUID =3D 19618BCE-55AE-09C6-37E9-4CE04084C7A1=
=0D
+ MODULE_TYPE =3D DXE_DRIVER=0D
+ VERSION_STRING =3D 1.0=0D
+ ENTRY_POINT =3D HttpCommandInitialize=0D
+ UNLOAD_IMAGE =3D HttpUnload=0D
+#=0D
+# This flag specifies whether HII resource section is generated into PE i=
mage.=0D
+#=0D
+ UEFI_HII_RESOURCE_SECTION =3D TRUE=0D
+=0D
+[Sources.common]=0D
+ Http.c=0D
+ HttpDynamicCommand.c=0D
+ Http.h=0D
+ Http.uni=0D
+=0D
+[Packages]=0D
+ EmbeddedPkg/EmbeddedPkg.dec=0D
+ MdePkg/MdePkg.dec=0D
+ MdeModulePkg/MdeModulePkg.dec=0D
+ NetworkPkg/NetworkPkg.dec=0D
+ ShellPkg/ShellPkg.dec=0D
+=0D
+[LibraryClasses]=0D
+ BaseLib=0D
+ BaseMemoryLib=0D
+ DebugLib=0D
+ FileHandleLib=0D
+ HiiLib=0D
+ HttpLib=0D
+ MemoryAllocationLib=0D
+ NetLib=0D
+ ShellLib=0D
+ UefiBootServicesTableLib=0D
+ UefiDriverEntryPoint=0D
+ UefiHiiServicesLib=0D
+ UefiLib=0D
+ UefiRuntimeServicesTableLib=0D
+=0D
+[Protocols]=0D
+ gEfiHiiPackageListProtocolGuid ## CONSUMES=0D
+ gEfiHttpProtocolGuid ## CONSUMES=0D
+ gEfiHttpServiceBindingProtocolGuid ## CONSUMES=0D
+ gEfiManagedNetworkServiceBindingProtocolGuid ## CONSUMES=0D
+ gEfiShellDynamicCommandProtocolGuid ## PRODUCES=0D
+=0D
+[DEPEX]=0D
+ TRUE=0D
diff --git a/ShellPkg/Include/Guid/ShellLibHiiGuid.h b/ShellPkg/Include/Gui=
d/ShellLibHiiGuid.h
index 5da9128333a4..6e328b460d8c 100644
--- a/ShellPkg/Include/Guid/ShellLibHiiGuid.h
+++ b/ShellPkg/Include/Guid/ShellLibHiiGuid.h
@@ -59,6 +59,10 @@
0x738a9314, 0x82c1, 0x4592, { 0x8f, 0xf7, 0xc1, 0xbd, 0xf1, 0xb2, 0x0e=
, 0xd4 } \=0D
}=0D
=0D
+#define SHELL_HTTP_HII_GUID \=0D
+ { \=0D
+ 0x390f84b3, 0x221c, 0x4d9e, { 0xb5, 0x06, 0x6d, 0xb9, 0x42, 0x3e, 0x0a=
, 0x7e } \=0D
+ }=0D
=0D
#define SHELL_BCFG_HII_GUID \=0D
{ \=0D
@@ -75,6 +79,7 @@ extern EFI_GUID gShellLevel3HiiGuid;
extern EFI_GUID gShellNetwork1HiiGuid;=0D
extern EFI_GUID gShellNetwork2HiiGuid;=0D
extern EFI_GUID gShellTftpHiiGuid;=0D
+extern EFI_GUID gShellHttpHiiGuid;=0D
extern EFI_GUID gShellBcfgHiiGuid;=0D
=0D
#endif=0D
diff --git a/ShellPkg/ShellPkg.dec b/ShellPkg/ShellPkg.dec
index d0843d338126..7b2d1230bd2c 100644
--- a/ShellPkg/ShellPkg.dec
+++ b/ShellPkg/ShellPkg.dec
@@ -53,6 +53,7 @@ [Guids]
gShellNetwork1HiiGuid =3D {0xf3d301bb, 0xf4a5, 0x45a8, {0xb0, =
0xb7, 0xfa, 0x99, 0x9c, 0x62, 0x37, 0xae}}=0D
gShellNetwork2HiiGuid =3D {0x174b2b5, 0xf505, 0x4b12, {0xaa, 0=
x60, 0x59, 0xdf, 0xf8, 0xd6, 0xea, 0x37}}=0D
gShellTftpHiiGuid =3D {0x738a9314, 0x82c1, 0x4592, {0x8f, =
0xf7, 0xc1, 0xbd, 0xf1, 0xb2, 0x0e, 0xd4}}=0D
+ gShellHttpHiiGuid =3D {0x390f84b3, 0x221c, 0x4d9e, {0xb5, =
0x06, 0x6d, 0xb9, 0x42, 0x3e, 0x0a, 0x7e}}=0D
gShellBcfgHiiGuid =3D {0x5f5f605d, 0x1583, 0x4a2d, {0xa6, =
0xb2, 0xeb, 0x12, 0xda, 0xb4, 0xa2, 0xb6}}=0D
gShellAcpiViewHiiGuid =3D {0xda8ccdf4, 0xed8f, 0x4ffc, {0xb5, =
0xef, 0x2e, 0xf5, 0x5e, 0x24, 0x93, 0x2a}}=0D
# FILE_GUID as defined in ShellPkg/Application/Shell/Shell.inf=0D
diff --git a/ShellPkg/ShellPkg.dsc b/ShellPkg/ShellPkg.dsc
index 86e9f1e0040d..c42bc9464a0f 100644
--- a/ShellPkg/ShellPkg.dsc
+++ b/ShellPkg/ShellPkg.dsc
@@ -139,6 +139,11 @@ [Components]
gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE=0D
}=0D
ShellPkg/DynamicCommand/TftpDynamicCommand/TftpApp.inf=0D
+ ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf {=0D
+ <PcdsFixedAtBuild>=0D
+ gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE=0D
+ }=0D
+ ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.inf=0D
ShellPkg/DynamicCommand/DpDynamicCommand/DpDynamicCommand.inf {=0D
<PcdsFixedAtBuild>=0D
gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE=0D
--=20
2.26.2.266.ge870325ee8


[PATCH v5 0/1] ShellPkg/DynamicCommand: add HttpDynamicCommand

Vladimir Olovyannikov
 

Signed-off-by: Vladimir Olovyannikov <vladimir.olovyannikov@...>
Cc: Samer El-Haj-Mahmoud <Samer.El-Haj-Mahmoud@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Zhichao Gao <zhichao.gao@...>
Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Ray Ni <ray.ni@...>
Cc: Liming Gao <liming.gao@...>
Cc: Nd <nd@...>

This patchset introduces an http client utilizing EDK2 HTTP protocol, to
allow fast image downloading from http/https servers.
HTTP download speed is usually faster than tftp.
The client is based on the same approach as tftp dynamic command, and
uses the same UEFI Shell command line parameters. This makes it easy
integrating http into existing UEFI Shell scripts.
Note that to enable HTTP download, feature Pcd
gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections must be set to TRUE.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2860

PATCH v5 changes:
Address Laszlo's comments on patch v4:
- Remove -m option as it introduced TimerLib dependency;
- Drop "Tested-by" flags.

Vladimir Olovyannikov (1):
ShellPkg/DynamicCommand: add HttpDynamicCommand

CryptoPkg/Library/OpensslLib/openssl | 2 +-
.../DynamicCommand/HttpDynamicCommand/Http.c | 1715 +++++++++++++++++
.../DynamicCommand/HttpDynamicCommand/Http.h | 89 +
.../HttpDynamicCommand/Http.uni | 117 ++
.../HttpDynamicCommand/HttpApp.c | 53 +
.../HttpDynamicCommand/HttpApp.inf | 58 +
.../HttpDynamicCommand/HttpDynamicCommand.c | 134 ++
.../HttpDynamicCommand/HttpDynamicCommand.inf | 63 +
ShellPkg/Include/Guid/ShellLibHiiGuid.h | 5 +
ShellPkg/ShellPkg.dec | 1 +
ShellPkg/ShellPkg.dsc | 5 +
11 files changed, 2241 insertions(+), 1 deletion(-)
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/Http.c
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/Http.h
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/Http.uni
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.c
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/HttpApp.inf
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.c
create mode 100644 ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicCommand.inf

--
2.26.2.266.ge870325ee8


Re: [PATCH V4 1/2] MdePkg/Include/IndustryStandard: CXL 1.1 Registers

Leif Lindholm
 

On Mon, Jul 27, 2020 at 14:55:03 +0000, Gao, Liming wrote:
diff --git a/MdePkg/Include/IndustryStandard/Cxl11.h b/MdePkg/Include/IndustryStandard/Cxl11.h
new file mode 100644
index 0000000000..933c1ab817
--- /dev/null
+++ b/MdePkg/Include/IndustryStandard/Cxl11.h
@@ -0,0 +1,569 @@
+/** @file
+ CXL 1.1 Register definitions
+
+ This file contains the register definitions based on the Compute Express Link
+ (CXL) Specification Revision 1.1.
+
+Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _CXL11_H_
+#define _CXL11_H_
We should not be adding macros with a leading _ - these are intended
for toolchain use.
This style is align to other header file. This is the file header
macro to make sure the header file be included more than once.
Yes. The other headers should also be changed, but in the meantime it
would be good to stop adding more incorrect ones.
https://edk2-docs.gitbook.io/edk-ii-c-coding-standards-specification/5_source_files/53_include_files#5-3-5-all-include-file-contents-must-be-protected-by-a-include-guard

+
+//
+// CXL Flex Bus Device default device and function number
+// Compute Express Link Specification Revision: 1.1 - Chapter 7.1.1
+//
+#define CXL_DEV_DEV 0
+#define CXL_DEV_FUNC 0
+
+//
+// Ensure proper structure formats
+//
+#pragma pack(1)
And this pragma has no function whatsoever with regards to any of the
register definition structs below. It would be much better if the
structs requiring packing (_DEVICE, _PORT, ...) were grouped together
and only those were given this treatment.

#pragma pack(1) is *not* a safe default.
I know pack(1) is for the compact structure layout.
Yes. And it should be used when structs need to be compacted.
All of the bitfield structs are single-variable structs, so the
packing has no effect on them, other than setting the struct alignment
requirements to 1 byte, which will not be correct (or efficient) on all
architectures.

Now, one final comment - and this is more of a project feature
suggestion:
Industry standard headers is something fairly special, even in
comparison with the rest of MdePkg. *I* would certainly like to ensure
I don't miss changes or additions to them.
Could we set up a dedicated group of reviewers for this folder only?
This need not affect the actual maintainership of MdePkg, just ensure
more eyeballs (or screen readers, braille terminals, ...) hit updates
here?

i.e. something like the below to Maintainers.txt:

F: MdePkg/Include/IndustryStandard/
R: Leif ...
R: ...
R: ...
This is a good suggestion. IndustryStandard needs more feedback.
Can you send the patch to update Maintainers.txt?
Sure, I can do that. Any thoughts on others to add than me?

/
Leif


Re: [PATCH v4 1/3] UefiPayloadPkg: Store the size of the MMCONF window

Guo Dong
 

Reviewed-by: Guo Dong <guo.dong@...>

-----Original Message-----
From: Marcello Sylvester Bauer <marcello.bauer@...>
Sent: Monday, July 27, 2020 1:19 AM
To: devel@edk2.groups.io
Cc: Patrick Rudolph <patrick.rudolph@...>; Christian Walter
<christian.walter@...>; Ma, Maurice <maurice.ma@...>;
Dong, Guo <guo.dong@...>; You, Benjamin <benjamin.you@...>
Subject: [PATCH v4 1/3] UefiPayloadPkg: Store the size of the MMCONF window

From: Patrick Rudolph <patrick.rudolph@...>

Store the real size of the Pcie Memory Mapped Address Space.
This change is necessary to support variable size of MMCONF spaces.

Signed-off-by: Patrick Rudolph <patrick.rudolph@...>
Signed-off-by: Marcello Sylvester Bauer <marcello.bauer@...>
Cc: Patrick Rudolph <patrick.rudolph@...>
Cc: Christian Walter <christian.walter@...>
Cc: Maurice Ma <maurice.ma@...>
Cc: Guo Dong <guo.dong@...>
Cc: Benjamin You <benjamin.you@...>
---
UefiPayloadPkg/Include/Guid/AcpiBoardInfoGuid.h | 1 +
UefiPayloadPkg/BlSupportPei/BlSupportPei.c | 3 +++
2 files changed, 4 insertions(+)

diff --git a/UefiPayloadPkg/Include/Guid/AcpiBoardInfoGuid.h
b/UefiPayloadPkg/Include/Guid/AcpiBoardInfoGuid.h
index fe783fe5e14c..043b748ae4a9 100644
--- a/UefiPayloadPkg/Include/Guid/AcpiBoardInfoGuid.h
+++ b/UefiPayloadPkg/Include/Guid/AcpiBoardInfoGuid.h
@@ -24,6 +24,7 @@ typedef struct {
UINT64 PmTimerRegBase;

UINT64 ResetRegAddress;

UINT64 PcieBaseAddress;

+ UINT64 PcieBaseSize;

} ACPI_BOARD_INFO;



#endif

diff --git a/UefiPayloadPkg/BlSupportPei/BlSupportPei.c
b/UefiPayloadPkg/BlSupportPei/BlSupportPei.c
index 22972453117a..a7e99f9ec6de 100644
--- a/UefiPayloadPkg/BlSupportPei/BlSupportPei.c
+++ b/UefiPayloadPkg/BlSupportPei/BlSupportPei.c
@@ -240,8 +240,10 @@ Done:
if (MmCfgHdr != NULL) {

MmCfgBase =
(EFI_ACPI_MEMORY_MAPPED_ENHANCED_CONFIGURATION_SPACE_BASE_AD
DRESS_ALLOCATION_STRUCTURE *)((UINT8*) MmCfgHdr + sizeof
(*MmCfgHdr));

AcpiBoardInfo->PcieBaseAddress = MmCfgBase->BaseAddress;

+ AcpiBoardInfo->PcieBaseSize = (MmCfgBase->EndBusNumber + 1 -
MmCfgBase->StartBusNumber) * 4096 * 32 * 8;

} else {

AcpiBoardInfo->PcieBaseAddress = 0;

+ AcpiBoardInfo->PcieBaseSize = 0;

}

DEBUG ((DEBUG_INFO, "PmCtrl Reg 0x%lx\n", AcpiBoardInfo-
PmCtrlRegBase));
DEBUG ((DEBUG_INFO, "PmTimer Reg 0x%lx\n", AcpiBoardInfo-
PmTimerRegBase));
@@ -250,6 +252,7 @@ Done:
DEBUG ((DEBUG_INFO, "PmEvt Reg 0x%lx\n", AcpiBoardInfo->PmEvtBase));

DEBUG ((DEBUG_INFO, "PmGpeEn Reg 0x%lx\n", AcpiBoardInfo-
PmGpeEnBase));
DEBUG ((DEBUG_INFO, "PcieBaseAddr 0x%lx\n", AcpiBoardInfo-
PcieBaseAddress));
+ DEBUG ((DEBUG_INFO, "PcieBaseSize 0x%lx\n", AcpiBoardInfo-
PcieBaseSize));


//

// Verify values for proper operation

--
2.27.0


Re: [PATCH v4 0/1] ShellPkg/DynamicCommand: add HttpDynamicCommand

Vladimir Olovyannikov
 

Hi Laszlo,

Thank you for valuable comments, I did not realize TimerLib should not be
used at all for ShellPkg.
I will remove -m option for now and then will replace with gRT time
functions.

-----Original Message-----
From: Laszlo Ersek <lersek@...>
Sent: Monday, July 27, 2020 1:39 AM
To: vladimir.olovyannikov@...; Zhichao Gao
<zhichao.gao@...>; Ray Ni <ray.ni@...>
Cc: devel@edk2.groups.io; Samer El-Haj-Mahmoud <Samer.El-Haj-
Mahmoud@...>; Maciej Rabeda <maciej.rabeda@...>;
Jiaxin Wu <jiaxin.wu@...>; Siyuan Fu <siyuan.fu@...>; Liming
Gao <liming.gao@...>; Nd <nd@...>
Subject: Re: [edk2-devel] [PATCH v4 0/1] ShellPkg/DynamicCommand: add
HttpDynamicCommand

Hi Vladimir,

On 07/23/20 22:50, Vladimir Olovyannikov via groups.io wrote:
Signed-off-by: Vladimir Olovyannikov
<vladimir.olovyannikov@...>
Tested-By: Samer El-Haj-Mahmoud <Samer.El-Haj-Mahmoud@...>
Tested-By: Laszlo Ersek <lersek@...>
Cc: Zhichao Gao <zhichao.gao@...>
Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Ray Ni <ray.ni@...>
Cc: Liming Gao <liming.gao@...>
Cc: Nd <nd@...>

This patchset introduces an http client utilizing EDK2 HTTP protocol,
to allow fast image downloading from http/https servers.
HTTP download speed is usually faster than tftp.
The client is based on the same approach as tftp dynamic command, and
uses the same UEFI Shell command line parameters. This makes it easy
integrating http into existing UEFI Shell scripts.
Note that to enable HTTP download, feature Pcd
gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections must be set to
TRUE.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2860

PATCH v4 changes:
Address comments based on Laszlo's testing:
- Fix .uni help file missing "\r\n" before RETURNVALUES section;
- delete the downloaded file in case of an error, unless
-k ("keep bad") option is provided on command line.

Allow download time measurement in seconds by providing -m parameter
on command line.
(1) "Tested-by" tags cannot be carried forward on a patch if there are any
changes to the patch. If you update a patch and people would like to have
their T-b's on the patch, then they'll have to retest the patch. So every
time
you update a patch, please drop the previously given Tested-by tags from
it.

OK,
(2) I was about to retest this patch, but I also compared it with the
previous
version (v3). I think the "-m" option should not be added, for now anyway.
For two reasons:

- The patch is already large, and I think there has been no ShellPkg
reviewer / maintainer feedback so far. I think we shouldn't make the
patch even larger, with new features. "-m" looks like an addition that
can be done separately, once the core feature is upstream.
OK, makes sense.

(I consider "-k" a bugfix on the other hand. More precisely, I
consider the new behavior *without "-k"* a bugfix. So I certainly
welcome that.)

- The other reason is that the "-m" feature seems to introduce a
TimerLib dependency. Depending on TimerLib in a shell application is
wrong, IMO; in particular because this application / dynamic command
is extremely useful, so some people might easily want to download a
pre-built binary, and run it on their system for whatever purposes.
But TimerLib is platform-dependent, and that would break this binary
portability.

For some more background, please refer to commit 7a141b1306f6
("ShellPkg: remove superfluous TimerLib resolution", 2018-02-13).
I read it, thanks. Now I understand the complications.


... In fact, upon re-reading the above commit, I'm realizing the current
patch
could break the "ShellPkg.dsc" build, because the patch
(incorrectly) introduces a TimerLib dependency in a ShellPkg module, but
"ShellPkg.dsc" (correctly) does not resolve the TimerLib class to any
instance.

And it does break:

$ build -a X64 -b NOOPT -t GCC48 -p ShellPkg/ShellPkg.dsc

ShellPkg/ShellPkg.dsc(...): error 4000: Instance of library class
[TimerLib] is
not found
in
[ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicComman
d.inf] [X64]
consumed by module
[ShellPkg/DynamicCommand/HttpDynamicCommand/HttpDynamicComman
d.inf]

One consequence of the above is that this patch would not pass a CI build.


... Another reason this patch would not pass a CI build is a
"PatchCheck.py"
failure:

ShellPkg/DynamicCommand: add HttpDynamicCommand The commit
message
format is not valid:
* 'Tested-By' should be 'Tested-by'
* 'Tested-By' should be 'Tested-by'
https://github.com/tianocore/tianocore.github.io/wiki/Commit-Message-F
ormat
The code passed all checks.
(Side comment: please use the clipboard for cutting and pasting feedback
tags from emails to commit messages. I have a keyboard shortcut for
inserting "Tested-by: Laszlo Ersek <lersek@...>", and so I know for
a fact that I never send an upper-case "By".)
OK, will do.

I suggest running a personal CI build on github.com before submitting the
patch to the list (just open a pull request -- if it fails, you'll get the
error
reports, and if it succeeds, then the mergify bot will auto-close the
successful
personal build, without actually merging the patch).
Thanks, will do that.


In summary, I suggest posting v5:

- with the Tested-by flags dropped (Samer and myself will have to
re-test),

- with the "-k" option preserved, but the "-m" option (and the
associated TimerLib dependency) removed.

Later on, I think "-m" could be added based on gRT->GetTime(), instead of
TimerLib.
Sure, totally makes sense.


Ray, Zhichao, when do you intend to review this patch? It does not make
much sense for Samer and myself to keep testing this patch if you're going
to
start the review only later. The review feedback will probably necessitate
updates to the patch, which will invalidate Samer's testing and mine.
We've
done a few rounds of testing; the patch certainly deserves package
maintainer review. Once no more changes looked necessary from the
reviewer side, I'd be happy to test the patch again.

Thanks
Laszlo
Thank you,
Vladimir


[PATCH v12 39/46] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Bypass flash detection with SEV-ES

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

The flash detection routine will attempt to determine how the flash
device behaves (e.g. ROM, RAM, Flash). But when SEV-ES is enabled and
the flash device behaves as a ROM device (meaning it is marked read-only
by the hypervisor), this check may result in an infinite nested page fault
because of the attempted write. Since the instruction cannot be emulated
when SEV-ES is enabled, the RIP is never advanced, resulting in repeated
nested page faults.

When SEV-ES is enabled, exit the flash detection early and assume that
the FD behaves as Flash. This will result in QemuFlashWrite() being called
to store EFI variables, which will also result in an infinite nested page
fault when the write is performed. In this case, update QemuFlashWrite()
to use the VMGEXIT MMIO write support to have the hypervisor perform the
write without having to emulate the instruction.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
.../FvbServicesRuntimeDxe.inf | 2 +
.../QemuFlash.h | 13 ++++++
.../QemuFlash.c | 23 +++++++++--
.../QemuFlashDxe.c | 40 +++++++++++++++++++
.../QemuFlashSmm.c | 16 ++++++++
5 files changed, 91 insertions(+), 3 deletions(-)

diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
index 72cabba4357d..8bb2325157ea 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
@@ -38,6 +38,7 @@ [Sources]
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
OvmfPkg/OvmfPkg.dec

[LibraryClasses]
@@ -52,6 +53,7 @@ [LibraryClasses]
UefiBootServicesTableLib
UefiDriverEntryPoint
UefiRuntimeLib
+ VmgExitLib

[Guids]
gEfiEventVirtualAddressChangeGuid # ALWAYS_CONSUMED
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
index f1afabcbe6ae..219d0d6e83cf 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
@@ -89,5 +89,18 @@ QemuFlashBeforeProbe (
IN UINTN FdBlockCount
);

+/**
+ Write to QEMU Flash
+
+ @param[in] Ptr Pointer to the location to write.
+ @param[in] Value The value to write.
+
+**/
+VOID
+QemuFlashPtrWrite (
+ IN volatile UINT8 *Ptr,
+ IN UINT8 Value
+ );
+
#endif

diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
index 1b0d6c053f1a..0d29bf701aca 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
@@ -9,6 +9,7 @@

#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
+#include <Library/MemEncryptSevLib.h>
#include <Library/PcdLib.h>

#include "QemuFlash.h"
@@ -80,6 +81,21 @@ QemuFlashDetected (

DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));

+ if (MemEncryptSevEsIsEnabled ()) {
+ //
+ // When SEV-ES is enabled, the check below can result in an infinite
+ // loop with respect to a nested page fault. When the memslot is mapped
+ // read-only, the nested page table entry is read-only. The check below
+ // will cause a nested page fault that cannot be emulated, causing
+ // the instruction to retried over and over. For SEV-ES, acknowledge that
+ // the FD appears as ROM and not as FLASH, but report FLASH anyway because
+ // FLASH behavior can be simulated using VMGEXIT.
+ //
+ DEBUG ((DEBUG_INFO,
+ "QEMU Flash: SEV-ES enabled, assuming FD behaves as FLASH\n"));
+ return TRUE;
+ }
+
OriginalUint8 = *Ptr;
*Ptr = CLEAR_STATUS_CMD;
ProbeUint8 = *Ptr;
@@ -181,8 +197,9 @@ QemuFlashWrite (
//
Ptr = QemuFlashPtr (Lba, Offset);
for (Loop = 0; Loop < *NumBytes; Loop++) {
- *Ptr = WRITE_BYTE_CMD;
- *Ptr = Buffer[Loop];
+ QemuFlashPtrWrite (Ptr, WRITE_BYTE_CMD);
+ QemuFlashPtrWrite (Ptr, Buffer[Loop]);
+
Ptr++;
}

@@ -190,7 +207,7 @@ QemuFlashWrite (
// Restore flash to read mode
//
if (*NumBytes > 0) {
- *(Ptr - 1) = READ_ARRAY_CMD;
+ QemuFlashPtrWrite (Ptr - 1, READ_ARRAY_CMD);
}

return EFI_SUCCESS;
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
index 5aabe9d7b59c..565383ee26d2 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
@@ -10,6 +10,9 @@
**/

#include <Library/UefiRuntimeLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/VmgExitLib.h>
+#include <Register/Amd/Msr.h>

#include "QemuFlash.h"

@@ -32,3 +35,40 @@ QemuFlashBeforeProbe (
// Do nothing
//
}
+
+/**
+ Write to QEMU Flash
+
+ @param[in] Ptr Pointer to the location to write.
+ @param[in] Value The value to write.
+
+**/
+VOID
+QemuFlashPtrWrite (
+ IN volatile UINT8 *Ptr,
+ IN UINT8 Value
+ )
+{
+ if (MemEncryptSevEsIsEnabled ()) {
+ MSR_SEV_ES_GHCB_REGISTER Msr;
+ GHCB *Ghcb;
+
+ Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+ Ghcb = Msr.Ghcb;
+
+ //
+ // Writing to flash is emulated by the hypervisor through the use of write
+ // protection. This won't work for an SEV-ES guest because the write won't
+ // be recognized as a true MMIO write, which would result in the required
+ // #VC exception. Instead, use the the VMGEXIT MMIO write support directly
+ // to perform the update.
+ //
+ VmgInit (Ghcb);
+ Ghcb->SharedBuffer[0] = Value;
+ Ghcb->SaveArea.SwScratch = (UINT64) (UINTN) Ghcb->SharedBuffer;
+ VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, (UINT64) (UINTN) Ptr, 1);
+ VmgDone (Ghcb);
+ } else {
+ *Ptr = Value;
+ }
+}
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
index 7eb426e03855..7eb80bfeffae 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashSmm.c
@@ -46,3 +46,19 @@ QemuFlashBeforeProbe (
);
ASSERT_EFI_ERROR (Status);
}
+
+/**
+ Write to QEMU Flash
+
+ @param[in] Ptr Pointer to the location to write.
+ @param[in] Value The value to write.
+
+**/
+VOID
+QemuFlashPtrWrite (
+ IN volatile UINT8 *Ptr,
+ IN UINT8 Value
+ )
+{
+ *Ptr = Value;
+}
--
2.27.0


[PATCH v12 38/46] OvmfPkg/Sec: Enable cache early to speed up booting

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Currently, the OVMF code relies on the hypervisor to enable the cache
support on the processor in order to improve the boot speed. However,
with SEV-ES, the hypervisor is not allowed to change the CR0 register
to enable caching.

Update the OVMF Sec support to enable caching in order to improve the
boot speed when running as an SEV-ES guest.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/Sec/SecMain.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c
index c2a35463dce4..271a06348ed8 100644
--- a/OvmfPkg/Sec/SecMain.c
+++ b/OvmfPkg/Sec/SecMain.c
@@ -905,6 +905,13 @@ SecCoreStartupWithStack (
// For non SEV-ES guests, just load the IDTR.
//
AsmWriteIdtr (&IdtDescriptor);
+ } else {
+ //
+ // Under SEV-ES, the hypervisor can't modify CR0 and so can't enable
+ // caching in order to speed up the boot. Enable caching early for
+ // an SEV-ES guest.
+ //
+ AsmEnableCache ();
}

DEBUG ((DEBUG_INFO,
--
2.27.0


[PATCH v12 37/46] OvmfPkg/Sec: Add #VC exception handling for Sec phase #vc

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

An SEV-ES guest will generate a #VC exception when it encounters a
non-automatic exit (NAE) event. It is expected that the #VC exception
handler will communicate with the hypervisor using the GHCB to handle
the NAE event.

NAE events can occur during the Sec phase, so initialize exception
handling early in the OVMF Sec support.

Before establishing the exception handling, validate that the supported
version of the SEV-ES protocol in OVMF is supported by the hypervisor.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/Sec/SecMain.inf | 4 +
OvmfPkg/Sec/SecMain.c | 181 +++++++++++++++++++++++++++++++++++++---
2 files changed, 172 insertions(+), 13 deletions(-)

diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf
index 63ba4cb555fb..7f78dcee2772 100644
--- a/OvmfPkg/Sec/SecMain.inf
+++ b/OvmfPkg/Sec/SecMain.inf
@@ -50,15 +50,19 @@ [LibraryClasses]
PeCoffExtraActionLib
ExtractGuidedSectionLib
LocalApicLib
+ CpuExceptionHandlerLib

[Ppis]
gEfiTemporaryRamSupportPpiGuid # PPI ALWAYS_PRODUCED

[Pcd]
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c
index 6dea6e771a29..c2a35463dce4 100644
--- a/OvmfPkg/Sec/SecMain.c
+++ b/OvmfPkg/Sec/SecMain.c
@@ -24,6 +24,9 @@
#include <Library/PeCoffExtraActionLib.h>
#include <Library/ExtractGuidedSectionLib.h>
#include <Library/LocalApicLib.h>
+#include <Library/CpuExceptionHandlerLib.h>
+#include <Register/Amd/Ghcb.h>
+#include <Register/Amd/Msr.h>

#include <Ppi/TemporaryRamSupport.h>

@@ -34,6 +37,10 @@ typedef struct _SEC_IDT_TABLE {
IA32_IDT_GATE_DESCRIPTOR IdtTable[SEC_IDT_ENTRY_COUNT];
} SEC_IDT_TABLE;

+typedef struct _SEC_SEV_ES_WORK_AREA {
+ UINT8 SevEsEnabled;
+} SEC_SEV_ES_WORK_AREA;
+
VOID
EFIAPI
SecStartupPhase2 (
@@ -712,6 +719,120 @@ FindAndReportEntryPoints (
return;
}

+/**
+ Handle an SEV-ES/GHCB protocol check failure.
+
+ Notify the hypervisor using the VMGEXIT instruction that the SEV-ES guest
+ wishes to be terminated.
+
+ @param[in] ReasonCode Reason code to provide to the hypervisor for the
+ termination request.
+
+**/
+STATIC
+VOID
+SevEsProtocolFailure (
+ IN UINT8 ReasonCode
+ )
+{
+ MSR_SEV_ES_GHCB_REGISTER Msr;
+
+ //
+ // Use the GHCB MSR Protocol to request termination by the hypervisor
+ //
+ Msr.GhcbPhysicalAddress = 0;
+ Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
+ Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
+ Msr.GhcbTerminate.ReasonCode = ReasonCode;
+ AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+ AsmVmgExit ();
+
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+}
+
+/**
+ Validate the SEV-ES/GHCB protocol level.
+
+ Verify that the level of SEV-ES/GHCB protocol supported by the hypervisor
+ and the guest intersect. If they don't intersect, request termination.
+
+**/
+STATIC
+VOID
+SevEsProtocolCheck (
+ VOID
+ )
+{
+ MSR_SEV_ES_GHCB_REGISTER Msr;
+ GHCB *Ghcb;
+
+ //
+ // Use the GHCB MSR Protocol to obtain the GHCB SEV-ES Information for
+ // protocol checking
+ //
+ Msr.GhcbPhysicalAddress = 0;
+ Msr.GhcbInfo.Function = GHCB_INFO_SEV_INFO_GET;
+ AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+ AsmVmgExit ();
+
+ Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
+
+ if (Msr.GhcbInfo.Function != GHCB_INFO_SEV_INFO) {
+ SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
+ }
+
+ if (Msr.GhcbProtocol.SevEsProtocolMin > Msr.GhcbProtocol.SevEsProtocolMax) {
+ SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
+ }
+
+ if ((Msr.GhcbProtocol.SevEsProtocolMin > GHCB_VERSION_MAX) ||
+ (Msr.GhcbProtocol.SevEsProtocolMax < GHCB_VERSION_MIN)) {
+ SevEsProtocolFailure (GHCB_TERMINATE_GHCB_PROTOCOL);
+ }
+
+ //
+ // SEV-ES protocol checking succeeded, set the initial GHCB address
+ //
+ Msr.GhcbPhysicalAddress = FixedPcdGet32 (PcdOvmfSecGhcbBase);
+ AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
+
+ Ghcb = Msr.Ghcb;
+ SetMem (Ghcb, sizeof (*Ghcb), 0);
+
+ //
+ // Set the version to the maximum that can be supported
+ //
+ Ghcb->ProtocolVersion = MIN (Msr.GhcbProtocol.SevEsProtocolMax, GHCB_VERSION_MAX);
+ Ghcb->GhcbUsage = GHCB_STANDARD_USAGE;
+}
+
+/**
+ Determine if SEV-ES is active.
+
+ During early booting, SEV-ES support code will set a flag to indicate that
+ SEV-ES is enabled. Return the value of this flag as an indicator that SEV-ES
+ is enabled.
+
+ @retval TRUE SEV-ES is enabled
+ @retval FALSE SEV-ES is not enabled
+
+**/
+STATIC
+BOOLEAN
+SevEsIsEnabled (
+ VOID
+ )
+{
+ SEC_SEV_ES_WORK_AREA *SevEsWorkArea;
+
+ SevEsWorkArea = (SEC_SEV_ES_WORK_AREA *) FixedPcdGet32 (PcdSevEsWorkAreaBase);
+
+ return ((SevEsWorkArea != NULL) && (SevEsWorkArea->SevEsEnabled != 0));
+}
+
VOID
EFIAPI
SecCoreStartupWithStack (
@@ -737,8 +858,55 @@ SecCoreStartupWithStack (
Table[Index] = 0;
}

+ //
+ // Initialize IDT - Since this is before library constructors are called,
+ // we use a loop rather than CopyMem.
+ //
+ IdtTableInStack.PeiService = NULL;
+ for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {
+ UINT8 *Src, *Dst;
+ UINTN Byte;
+
+ Src = (UINT8 *) &mIdtEntryTemplate;
+ Dst = (UINT8 *) &IdtTableInStack.IdtTable[Index];
+ for (Byte = 0; Byte < sizeof (mIdtEntryTemplate); Byte++) {
+ Dst[Byte] = Src[Byte];
+ }
+ }
+
+ IdtDescriptor.Base = (UINTN)&IdtTableInStack.IdtTable;
+ IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1);
+
+ if (SevEsIsEnabled ()) {
+ SevEsProtocolCheck ();
+
+ //
+ // For SEV-ES guests, the exception handler is needed before calling
+ // ProcessLibraryConstructorList() because some of the library constructors
+ // perform some functions that result in #VC exceptions being generated.
+ //
+ // Due to this code executing before library constructors, *all* library
+ // API calls are theoretically interface contract violations. However,
+ // because this is SEC (executing in flash), those constructors cannot
+ // write variables with static storage duration anyway. Furthermore, only
+ // a small, restricted set of APIs, such as AsmWriteIdtr() and
+ // InitializeCpuExceptionHandlers(), are called, where we require that the
+ // underlying library not require constructors to have been invoked and
+ // that the library instance not trigger any #VC exceptions.
+ //
+ AsmWriteIdtr (&IdtDescriptor);
+ InitializeCpuExceptionHandlers (NULL);
+ }
+
ProcessLibraryConstructorList (NULL, NULL);

+ if (!SevEsIsEnabled ()) {
+ //
+ // For non SEV-ES guests, just load the IDTR.
+ //
+ AsmWriteIdtr (&IdtDescriptor);
+ }
+
DEBUG ((DEBUG_INFO,
"SecCoreStartupWithStack(0x%x, 0x%x)\n",
(UINT32)(UINTN)BootFv,
@@ -751,19 +919,6 @@ SecCoreStartupWithStack (
//
InitializeFloatingPointUnits ();

- //
- // Initialize IDT
- //
- IdtTableInStack.PeiService = NULL;
- for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) {
- CopyMem (&IdtTableInStack.IdtTable[Index], &mIdtEntryTemplate, sizeof (mIdtEntryTemplate));
- }
-
- IdtDescriptor.Base = (UINTN)&IdtTableInStack.IdtTable;
- IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1);
-
- AsmWriteIdtr (&IdtDescriptor);
-
#if defined (MDE_CPU_X64)
//
// ASSERT that the Page Tables were set by the reset vector code to
--
2.27.0


[PATCH v12 36/46] OvmfPkg/ResetVector: Add support for a 32-bit SEV check

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

During BSP startup, the reset vector code will issue a CPUID instruction
while in 32-bit mode. When running as an SEV-ES guest, this will trigger
a #VC exception.

Add exception handling support to the early reset vector code to catch
these exceptions. Also, since the guest is in 32-bit mode at this point,
writes to the GHCB will be encrypted and thus not able to be read by the
hypervisor, so use the GHCB CPUID request/response protocol to obtain the
requested CPUID function values and provide these to the guest.

The exception handling support is active during the SEV check and uses the
OVMF temporary RAM space for a stack. After the SEV check is complete, the
exception handling support is removed and the stack pointer cleared.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/ResetVector/ResetVector.inf | 3 +
OvmfPkg/ResetVector/Ia32/PageTables64.asm | 275 +++++++++++++++++++++-
OvmfPkg/ResetVector/ResetVector.nasmb | 2 +
3 files changed, 277 insertions(+), 3 deletions(-)

diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
index 483fd90fe785..a53ae6c194ae 100644
--- a/OvmfPkg/ResetVector/ResetVector.inf
+++ b/OvmfPkg/ResetVector/ResetVector.inf
@@ -34,9 +34,12 @@ [BuildOptions]
*_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/

[Pcd]
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
index 9f86ddf6f08f..7c72128a84d6 100644
--- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
+++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
@@ -36,13 +36,58 @@ BITS 32
PAGE_READ_WRITE + \
PAGE_PRESENT)

+;
+; SEV-ES #VC exception handler support
+;
+; #VC handler local variable locations
+;
+%define VC_CPUID_RESULT_EAX 0
+%define VC_CPUID_RESULT_EBX 4
+%define VC_CPUID_RESULT_ECX 8
+%define VC_CPUID_RESULT_EDX 12
+%define VC_GHCB_MSR_EDX 16
+%define VC_GHCB_MSR_EAX 20
+%define VC_CPUID_REQUEST_REGISTER 24
+%define VC_CPUID_FUNCTION 28
+
+; #VC handler total local variable size
+;
+%define VC_VARIABLE_SIZE 32
+
+; #VC handler GHCB CPUID request/response protocol values
+;
+%define GHCB_CPUID_REQUEST 4
+%define GHCB_CPUID_RESPONSE 5
+%define GHCB_CPUID_REGISTER_SHIFT 30
+%define CPUID_INSN_LEN 2
+
+
; Check if Secure Encrypted Virtualization (SEV) feature is enabled
;
-; If SEV is enabled then EAX will be at least 32
+; Modified: EAX, EBX, ECX, EDX, ESP
+;
+; If SEV is enabled then EAX will be at least 32.
; If SEV is disabled then EAX will be zero.
;
CheckSevFeature:
+ ; Set the first byte of the workarea to zero to communicate to the SEC
+ ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID
+ ; instruction will trigger a #VC exception where the first byte of the
+ ; workarea will be set to one.
+ mov byte[SEV_ES_WORK_AREA], 0
+
+ ;
+ ; Set up exception handlers to check for SEV-ES
+ ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for
+ ; stack usage)
+ ; Establish exception handlers
+ ;
+ mov esp, SEV_ES_VC_TOP_OF_STACK
+ mov eax, ADDR_OF(Idtr)
+ lidt [cs:eax]
+
; Check if we have a valid (0x8000_001F) CPUID leaf
+ ; CPUID raises a #VC exception if running as an SEV-ES guest
mov eax, 0x80000000
cpuid

@@ -53,8 +98,8 @@ CheckSevFeature:
jl NoSev

; Check for memory encryption feature:
- ; CPUID Fn8000_001F[EAX] - Bit 1
- ;
+ ; CPUID Fn8000_001F[EAX] - Bit 1
+ ; CPUID raises a #VC exception if running as an SEV-ES guest
mov eax, 0x8000001f
cpuid
bt eax, 1
@@ -78,6 +123,15 @@ NoSev:
xor eax, eax

SevExit:
+ ;
+ ; Clear exception handlers and stack
+ ;
+ push eax
+ mov eax, ADDR_OF(IdtrClear)
+ lidt [cs:eax]
+ pop eax
+ mov esp, 0
+
OneTimeCallRet CheckSevFeature

; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
@@ -222,3 +276,218 @@ SetCr3:
mov cr3, eax

OneTimeCallRet SetCr3ForPageTables64
+
+;
+; Start of #VC exception handling routines
+;
+
+SevEsIdtNotCpuid:
+ ;
+ ; Use VMGEXIT to request termination.
+ ; 1 - #VC was not for CPUID
+ ;
+ mov eax, 1
+ jmp SevEsIdtTerminate
+
+SevEsIdtNoCpuidResponse:
+ ;
+ ; Use VMGEXIT to request termination.
+ ; 2 - GHCB_CPUID_RESPONSE not received
+ ;
+ mov eax, 2
+
+SevEsIdtTerminate:
+ ;
+ ; Use VMGEXIT to request termination. At this point the reason code is
+ ; located in EAX, so shift it left 16 bits to the proper location.
+ ;
+ ; EAX[11:0] => 0x100 - request termination
+ ; EAX[15:12] => 0x1 - OVMF
+ ; EAX[23:16] => 0xXX - REASON CODE
+ ;
+ shl eax, 16
+ or eax, 0x1100
+ xor edx, edx
+ mov ecx, 0xc0010130
+ wrmsr
+ ;
+ ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
+ ; mode, so work around this by temporarily switching to 64-bit mode.
+ ;
+BITS 64
+ rep vmmcall
+BITS 32
+
+ ;
+ ; We shouldn't come back from the VMGEXIT, but if we do, just loop.
+ ;
+SevEsIdtHlt:
+ hlt
+ jmp SevEsIdtHlt
+ iret
+
+ ;
+ ; Total stack usage for the #VC handler is 44 bytes:
+ ; - 12 bytes for the exception IRET (after popping error code)
+ ; - 32 bytes for the local variables.
+ ;
+SevEsIdtVmmComm:
+ ;
+ ; If we're here, then we are an SEV-ES guest and this
+ ; was triggered by a CPUID instruction
+ ;
+ ; Set the first byte of the workarea to one to communicate to the SEC
+ ; phase that SEV-ES is enabled.
+ mov byte[SEV_ES_WORK_AREA], 1
+
+ pop ecx ; Error code
+ cmp ecx, 0x72 ; Be sure it was CPUID
+ jne SevEsIdtNotCpuid
+
+ ; Set up local variable room on the stack
+ ; CPUID function : + 28
+ ; CPUID request register : + 24
+ ; GHCB MSR (EAX) : + 20
+ ; GHCB MSR (EDX) : + 16
+ ; CPUID result (EDX) : + 12
+ ; CPUID result (ECX) : + 8
+ ; CPUID result (EBX) : + 4
+ ; CPUID result (EAX) : + 0
+ sub esp, VC_VARIABLE_SIZE
+
+ ; Save the CPUID function being requested
+ mov [esp + VC_CPUID_FUNCTION], eax
+
+ ; The GHCB CPUID protocol uses the following mapping to request
+ ; a specific register:
+ ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX
+ ;
+ ; Set EAX as the first register to request. This will also be used as a
+ ; loop variable to request all register values (EAX to EDX).
+ xor eax, eax
+ mov [esp + VC_CPUID_REQUEST_REGISTER], eax
+
+ ; Save current GHCB MSR value
+ mov ecx, 0xc0010130
+ rdmsr
+ mov [esp + VC_GHCB_MSR_EAX], eax
+ mov [esp + VC_GHCB_MSR_EDX], edx
+
+NextReg:
+ ;
+ ; Setup GHCB MSR
+ ; GHCB_MSR[63:32] = CPUID function
+ ; GHCB_MSR[31:30] = CPUID register
+ ; GHCB_MSR[11:0] = CPUID request protocol
+ ;
+ mov eax, [esp + VC_CPUID_REQUEST_REGISTER]
+ cmp eax, 4
+ jge VmmDone
+
+ shl eax, GHCB_CPUID_REGISTER_SHIFT
+ or eax, GHCB_CPUID_REQUEST
+ mov edx, [esp + VC_CPUID_FUNCTION]
+ mov ecx, 0xc0010130
+ wrmsr
+
+ ;
+ ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit
+ ; mode, so work around this by temporarily switching to 64-bit mode.
+ ;
+BITS 64
+ rep vmmcall
+BITS 32
+
+ ;
+ ; Read GHCB MSR
+ ; GHCB_MSR[63:32] = CPUID register value
+ ; GHCB_MSR[31:30] = CPUID register
+ ; GHCB_MSR[11:0] = CPUID response protocol
+ ;
+ mov ecx, 0xc0010130
+ rdmsr
+ mov ecx, eax
+ and ecx, 0xfff
+ cmp ecx, GHCB_CPUID_RESPONSE
+ jne SevEsIdtNoCpuidResponse
+
+ ; Save returned value
+ shr eax, GHCB_CPUID_REGISTER_SHIFT
+ mov [esp + eax * 4], edx
+
+ ; Next register
+ inc word [esp + VC_CPUID_REQUEST_REGISTER]
+
+ jmp NextReg
+
+VmmDone:
+ ;
+ ; At this point we have all CPUID register values. Restore the GHCB MSR,
+ ; set the return register values and return.
+ ;
+ mov eax, [esp + VC_GHCB_MSR_EAX]
+ mov edx, [esp + VC_GHCB_MSR_EDX]
+ mov ecx, 0xc0010130
+ wrmsr
+
+ mov eax, [esp + VC_CPUID_RESULT_EAX]
+ mov ebx, [esp + VC_CPUID_RESULT_EBX]
+ mov ecx, [esp + VC_CPUID_RESULT_ECX]
+ mov edx, [esp + VC_CPUID_RESULT_EDX]
+
+ add esp, VC_VARIABLE_SIZE
+
+ ; Update the EIP value to skip over the now handled CPUID instruction
+ ; (the CPUID instruction has a length of 2)
+ add word [esp], CPUID_INSN_LEN
+ iret
+
+ALIGN 2
+
+Idtr:
+ dw IDT_END - IDT_BASE - 1 ; Limit
+ dd ADDR_OF(IDT_BASE) ; Base
+
+IdtrClear:
+ dw 0 ; Limit
+ dd 0 ; Base
+
+ALIGN 16
+
+;
+; The Interrupt Descriptor Table (IDT)
+; This will be used to determine if SEV-ES is enabled. Upon execution
+; of the CPUID instruction, a VMM Communication Exception will occur.
+; This will tell us if SEV-ES is enabled. We can use the current value
+; of the GHCB MSR to determine the SEV attributes.
+;
+IDT_BASE:
+;
+; Vectors 0 - 28 (No handlers)
+;
+%rep 29
+ dw 0 ; Offset low bits 15..0
+ dw 0x10 ; Selector
+ db 0 ; Reserved
+ db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+ dw 0 ; Offset high bits 31..16
+%endrep
+;
+; Vector 29 (VMM Communication Exception)
+;
+ dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0
+ dw 0x10 ; Selector
+ db 0 ; Reserved
+ db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+ dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16
+;
+; Vectors 30 - 31 (No handlers)
+;
+%rep 2
+ dw 0 ; Offset low bits 15..0
+ dw 0x10 ; Selector
+ db 0 ; Reserved
+ db 0x8E ; Gate Type (IA32_IDT_GATE_TYPE_INTERRUPT_32)
+ dw 0 ; Offset high bits 31..16
+%endrep
+IDT_END:
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index bfb77e439105..762661115d50 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -72,6 +72,8 @@
%define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
%define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
%define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
+ %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase))
+ %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
%include "Ia32/PageTables64.asm"
%endif

--
2.27.0


[PATCH v12 35/46] OvmfPkg/PlatformPei: Reserve SEV-ES work area if S3 is supported

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Protect the SEV-ES work area memory used by an SEV-ES guest.

Regarding the lifecycle of the SEV-ES memory area:
PcdSevEsWorkArea

(a) when and how it is initialized after first boot of the VM

If SEV-ES is enabled, the SEV-ES area is initialized during
the SEC phase [OvmfPkg/ResetVector/Ia32/PageTables64.asm].

(b) how it is protected from memory allocations during DXE

If SEV-ES is enabled, then InitializeRamRegions()
[OvmfPkg/PlatformPei/MemDetect.c] protects the ranges with either
an AcpiNVS (S3 enabled) or BootServicesData (S3 disabled) memory
allocation HOB, in PEI.

(c) how it is protected from the OS

If S3 is enabled, then (b) reserves it from the OS too.

If S3 is disabled, then the range needs no protection.

(d) how it is accessed on the S3 resume path

It is rewritten same as in (a), which is fine because (b) reserved it.

(e) how it is accessed on the warm reset path

It is rewritten same as in (a).

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Cc: Anthony Perard <anthony.perard@...>
Cc: Julien Grall <julien@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/PlatformPei/PlatformPei.inf | 2 ++
OvmfPkg/PlatformPei/MemDetect.c | 20 ++++++++++++++++++++
2 files changed, 22 insertions(+)

diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index 4742e1bdf42b..c53be2f4925c 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -118,6 +118,8 @@ [FixedPcd]
gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiReservedMemoryType
gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode
gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize

[FeaturePcd]
gUefiOvmfPkgTokenSpaceGuid.PcdCsmEnable
diff --git a/OvmfPkg/PlatformPei/MemDetect.c b/OvmfPkg/PlatformPei/MemDetect.c
index 6b5fee166b5d..ffbbef891a11 100644
--- a/OvmfPkg/PlatformPei/MemDetect.c
+++ b/OvmfPkg/PlatformPei/MemDetect.c
@@ -940,5 +940,25 @@ InitializeRamRegions (
);
}
}
+
+#ifdef MDE_CPU_X64
+ if (MemEncryptSevEsIsEnabled ()) {
+ //
+ // If SEV-ES is enabled, reserve the SEV-ES work area.
+ //
+ // Since this memory range will be used by the Reset Vector on S3
+ // resume, it must be reserved as ACPI NVS.
+ //
+ // If S3 is unsupported, then various drivers might still write to the
+ // work area. We ought to prevent DXE from serving allocation requests
+ // such that they would overlap the work area.
+ //
+ BuildMemoryAllocationHob (
+ (EFI_PHYSICAL_ADDRESS)(UINTN) FixedPcdGet32 (PcdSevEsWorkAreaBase),
+ (UINT64)(UINTN) FixedPcdGet32 (PcdSevEsWorkAreaSize),
+ mS3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
+ );
+ }
+#endif
}
}
--
2.27.0


[PATCH v12 34/46] OvmfPkg: Reserve a page in memory for the SEV-ES usage

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Reserve a fixed area of memory for SEV-ES use and set a fixed PCD,
PcdSevEsWorkAreaBase, to this value.

This area will be used by SEV-ES support for two purposes:
1. Communicating the SEV-ES status during BSP boot to SEC:
Using a byte of memory from the page, the BSP reset vector code can
communicate the SEV-ES status to SEC for use before exception
handling can be enabled in SEC. After SEC, this field is no longer
valid and the standard way of determine if SEV-ES is active should
be used.

2. Establishing an area of memory for AP boot support:
A hypervisor is not allowed to update an SEV-ES guest's register
state, so when booting an SEV-ES guest AP, the hypervisor is not
allowed to set the RIP to the guest requested value. Instead an
SEV-ES AP must be re-directed from within the guest to the actual
requested staring location as specified in the INIT-SIPI-SIPI
sequence.

Use this memory for reset vector code that can be programmed to have
the AP jump to the desired RIP location after starting the AP. This
is required for only the very first AP reset.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/OvmfPkgX64.fdf | 3 +++
1 file changed, 3 insertions(+)

diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index edb03b5464d4..8da59037e5f0 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -82,6 +82,9 @@ [FD.MEMFD]
0x009000|0x002000
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize

+0x00B000|0x001000
+gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
+
0x010000|0x010000
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize

--
2.27.0


[PATCH v12 33/46] UefiCpuPkg: Create an SEV-ES workarea PCD

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Create an SEV-ES workarea PCD. This PCD will be used for BSP communication
during SEC and for AP startup during PEI and DXE phases, the latter is the
reason for creating it in the UefiCpuPkg.

Cc: Eric Dong <eric.dong@...>
Cc: Ray Ni <ray.ni@...>
Cc: Laszlo Ersek <lersek@...>
Reviewed-by: Eric Dong <eric.dong@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
UefiCpuPkg/UefiCpuPkg.dec | 8 ++++++++
UefiCpuPkg/UefiCpuPkg.uni | 8 ++++++++
2 files changed, 16 insertions(+)

diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index cb92f34b6f55..8c614f9b42bd 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -161,6 +161,14 @@ [PcdsFixedAtBuild]
# @Prompt Specify the count of pre allocated SMM MP tokens per chunk.
gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmMpTokenCountPerChunk|64|UINT32|0x30002002

+ ## Area of memory where the SEV-ES work area block lives.
+ # @Prompt Configure the SEV-ES work area base
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|0x0|UINT32|0x30002005
+
+ ## Size of teh area of memory where the SEV-ES work area block lives.
+ # @Prompt Configure the SEV-ES work area base
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize|0x0|UINT32|0x30002006
+
[PcdsFixedAtBuild, PcdsPatchableInModule]
## This value is the CPU Local APIC base address, which aligns the address on a 4-KByte boundary.
# @Prompt Configure base address of CPU Local APIC
diff --git a/UefiCpuPkg/UefiCpuPkg.uni b/UefiCpuPkg/UefiCpuPkg.uni
index f4a0c72f6293..219c1963bf08 100644
--- a/UefiCpuPkg/UefiCpuPkg.uni
+++ b/UefiCpuPkg/UefiCpuPkg.uni
@@ -281,3 +281,11 @@

#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsIsEnabled_PROMPT #language en-US "Specifies whether SEV-ES is enabled"
#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsIsEnabled_HELP #language en-US "Set to TRUE when running as an SEV-ES guest, FALSE otherwise."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaBase_PROMPT #language en-US "Specify the address of the SEV-ES work area"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaBase_HELP #language en-US "Specifies the address of the work area used by an SEV-ES guest."
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaSize_PROMPT #language en-US "Specify the size of the SEV-ES work area"
+
+#string STR_gUefiCpuPkgTokenSpaceGuid_PcdSevEsWorkAreaSize_HELP #language en-US "Specifies the size of the work area used by an SEV-ES guest."
--
2.27.0


[PATCH v12 32/46] OvmfPkg/PlatformPei: Move early GDT into ram when SEV-ES is enabled

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

The SEV support will clear the C-bit from non-RAM areas. The early GDT
lives in a non-RAM area, so when an exception occurs (like a #VC) the GDT
will be read as un-encrypted even though it is encrypted. This will result
in a failure to be able to handle the exception.

Move the GDT into RAM so it can be accessed without error when running as
an SEV-ES guest.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/PlatformPei/AmdSev.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index 4fd4534cabea..a2b38c591236 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -39,6 +39,8 @@ AmdSevEsInitialize (
PHYSICAL_ADDRESS GhcbBasePa;
UINTN GhcbPageCount, PageCount;
RETURN_STATUS PcdStatus, DecryptStatus;
+ IA32_DESCRIPTOR Gdtr;
+ VOID *Gdt;

if (!MemEncryptSevEsIsEnabled ()) {
return;
@@ -83,6 +85,22 @@ AmdSevEsInitialize (
(UINT64)GhcbPageCount, GhcbBase));

AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
+
+ //
+ // The SEV support will clear the C-bit from non-RAM areas. The early GDT
+ // lives in a non-RAM area, so when an exception occurs (like a #VC) the GDT
+ // will be read as un-encrypted even though it was created before the C-bit
+ // was cleared (encrypted). This will result in a failure to be able to
+ // handle the exception.
+ //
+ AsmReadGdtr (&Gdtr);
+
+ Gdt = AllocatePages (EFI_SIZE_TO_PAGES ((UINTN) Gdtr.Limit + 1));
+ ASSERT (Gdt != NULL);
+
+ CopyMem (Gdt, (VOID *) Gdtr.Base, Gdtr.Limit + 1);
+ Gdtr.Base = (UINTN) Gdt;
+ AsmWriteGdtr (&Gdtr);
}

/**
--
2.27.0


[PATCH v12 31/46] OvmfPkg: Create GHCB pages for use during Pei and Dxe phase

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Allocate memory for the GHCB pages and the per-CPU variable pages during
SEV initialization for use during Pei and Dxe phases. The GHCB page(s)
must be shared pages, so clear the encryption mask from the current page
table entries. Upon successful allocation, set the GHCB PCDs (PcdGhcbBase
and PcdGhcbSize).

The per-CPU variable page needs to be unique per AP. Using the page after
the GHCB ensures that it is unique per AP. Only the GHCB page is marked as
shared, keeping the per-CPU variable page encyrpted. The same logic is
used in DXE using CreateIdentityMappingPageTables() before switching to
the DXE pagetables.

The GHCB pages (one per vCPU) will be used by the PEI and DXE #VC
exception handlers. The #VC exception handler will fill in the necessary
fields of the GHCB and exit to the hypervisor using the VMGEXIT
instruction. The hypervisor then accesses the GHCB associated with the
vCPU in order to perform the requested function.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/OvmfPkgIa32.dsc | 2 ++
OvmfPkg/OvmfPkgIa32X64.dsc | 2 ++
OvmfPkg/OvmfPkgX64.dsc | 2 ++
OvmfPkg/PlatformPei/PlatformPei.inf | 2 ++
OvmfPkg/PlatformPei/AmdSev.c | 45 ++++++++++++++++++++++++++++-
5 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index f84f23f250ef..133a9a93c071 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -608,6 +608,8 @@ [PcdsDynamicDefault]
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0

# Set SEV-ES defaults
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0

!if $(SMM_REQUIRE) == TRUE
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index a66abccf8266..338c38db29b5 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -620,6 +620,8 @@ [PcdsDynamicDefault]
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0

# Set SEV-ES defaults
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0

!if $(SMM_REQUIRE) == TRUE
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 2a8975fd3d29..b80710fbdca4 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -618,6 +618,8 @@ [PcdsDynamicDefault]
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0

# Set SEV-ES defaults
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase|0
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize|0
gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0

!if $(SMM_REQUIRE) == TRUE
diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index a54d10ba90d5..4742e1bdf42b 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -102,6 +102,8 @@ [Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdGhcbSize
gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy
gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress
gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index 4dc5340caa7a..4fd4534cabea 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -10,12 +10,15 @@
// The package level header files this module uses
//
#include <IndustryStandard/Q35MchIch9.h>
+#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/MemEncryptSevLib.h>
+#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <PiPei.h>
#include <Register/Amd/Cpuid.h>
+#include <Register/Amd/Msr.h>
#include <Register/Cpuid.h>
#include <Register/Intel/SmramSaveStateMap.h>

@@ -32,7 +35,10 @@ AmdSevEsInitialize (
VOID
)
{
- RETURN_STATUS PcdStatus;
+ VOID *GhcbBase;
+ PHYSICAL_ADDRESS GhcbBasePa;
+ UINTN GhcbPageCount, PageCount;
+ RETURN_STATUS PcdStatus, DecryptStatus;

if (!MemEncryptSevEsIsEnabled ()) {
return;
@@ -40,6 +46,43 @@ AmdSevEsInitialize (

PcdStatus = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
ASSERT_RETURN_ERROR (PcdStatus);
+
+ //
+ // Allocate GHCB and per-CPU variable pages.
+ //
+ GhcbPageCount = mMaxCpuCount * 2;
+ GhcbBase = AllocatePages (GhcbPageCount);
+ ASSERT (GhcbBase != NULL);
+
+ GhcbBasePa = (PHYSICAL_ADDRESS)(UINTN) GhcbBase;
+
+ //
+ // Each vCPU gets two consecutive pages, the first is the GHCB and the
+ // second is the per-CPU variable page. Loop through the allocation and
+ // only clear the encryption mask for the GHCB pages.
+ //
+ for (PageCount = 0; PageCount < GhcbPageCount; PageCount += 2) {
+ DecryptStatus = MemEncryptSevClearPageEncMask (
+ 0,
+ GhcbBasePa + EFI_PAGES_TO_SIZE (PageCount),
+ 1,
+ TRUE
+ );
+ ASSERT_RETURN_ERROR (DecryptStatus);
+ }
+
+ ZeroMem (GhcbBase, EFI_PAGES_TO_SIZE (GhcbPageCount));
+
+ PcdStatus = PcdSet64S (PcdGhcbBase, GhcbBasePa);
+ ASSERT_RETURN_ERROR (PcdStatus);
+ PcdStatus = PcdSet64S (PcdGhcbSize, EFI_PAGES_TO_SIZE (GhcbPageCount));
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ DEBUG ((DEBUG_INFO,
+ "SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",
+ (UINT64)GhcbPageCount, GhcbBase));
+
+ AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
}

/**
--
2.27.0


[PATCH v12 30/46] OvmfPkg/PlatformPei: Reserve GHCB-related areas if S3 is supported

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Protect the memory used by an SEV-ES guest when S3 is supported. This
includes the page table used to break down the 2MB page that contains
the GHCB so that it can be marked un-encrypted, as well as the GHCB
area.

Regarding the lifecycle of the GHCB-related memory areas:
PcdOvmfSecGhcbPageTableBase
PcdOvmfSecGhcbBase

(a) when and how it is initialized after first boot of the VM

If SEV-ES is enabled, the GHCB-related areas are initialized during
the SEC phase [OvmfPkg/ResetVector/Ia32/PageTables64.asm].

(b) how it is protected from memory allocations during DXE

If S3 and SEV-ES are enabled, then InitializeRamRegions()
[OvmfPkg/PlatformPei/MemDetect.c] protects the ranges with an AcpiNVS
memory allocation HOB, in PEI.

If S3 is disabled, then these ranges are not protected. DXE's own page
tables are first built while still in PEI (see HandOffToDxeCore()
[MdeModulePkg/Core/DxeIplPeim/X64/DxeLoadFunc.c]). Those tables are
located in permanent PEI memory. After CR3 is switched over to them
(which occurs before jumping to the DXE core entry point), we don't have
to preserve PcdOvmfSecGhcbPageTableBase. PEI switches to GHCB pages in
permanent PEI memory and DXE will use these PEI GHCB pages, so we don't
have to preserve PcdOvmfSecGhcbBase.

(c) how it is protected from the OS

If S3 is enabled, then (b) reserves it from the OS too.

If S3 is disabled, then the range needs no protection.

(d) how it is accessed on the S3 resume path

It is rewritten same as in (a), which is fine because (b) reserved it.

(e) how it is accessed on the warm reset path

It is rewritten same as in (a).

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Cc: Anthony Perard <anthony.perard@...>
Cc: Julien Grall <julien@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/PlatformPei/PlatformPei.inf | 4 ++++
OvmfPkg/PlatformPei/MemDetect.c | 23 +++++++++++++++++++++++
2 files changed, 27 insertions(+)

diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index 00feb96c9308..a54d10ba90d5 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -75,6 +75,10 @@ [Pcd]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfLockBoxStorageSize
gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
diff --git a/OvmfPkg/PlatformPei/MemDetect.c b/OvmfPkg/PlatformPei/MemDetect.c
index 3b46ea431ade..6b5fee166b5d 100644
--- a/OvmfPkg/PlatformPei/MemDetect.c
+++ b/OvmfPkg/PlatformPei/MemDetect.c
@@ -27,6 +27,7 @@ Module Name:
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/IoLib.h>
+#include <Library/MemEncryptSevLib.h>
#include <Library/PcdLib.h>
#include <Library/PciLib.h>
#include <Library/PeimEntryPoint.h>
@@ -866,6 +867,28 @@ InitializeRamRegions (
(UINT64)(UINTN) PcdGet32 (PcdOvmfSecPageTablesSize),
EfiACPIMemoryNVS
);
+
+ if (MemEncryptSevEsIsEnabled ()) {
+ //
+ // If SEV-ES is enabled, reserve the GHCB-related memory area. This
+ // includes the extra page table used to break down the 2MB page
+ // mapping into 4KB page entries where the GHCB resides and the
+ // GHCB area itself.
+ //
+ // Since this memory range will be used by the Reset Vector on S3
+ // resume, it must be reserved as ACPI NVS.
+ //
+ BuildMemoryAllocationHob (
+ (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecGhcbPageTableBase),
+ (UINT64)(UINTN) PcdGet32 (PcdOvmfSecGhcbPageTableSize),
+ EfiACPIMemoryNVS
+ );
+ BuildMemoryAllocationHob (
+ (EFI_PHYSICAL_ADDRESS)(UINTN) PcdGet32 (PcdOvmfSecGhcbBase),
+ (UINT64)(UINTN) PcdGet32 (PcdOvmfSecGhcbSize),
+ EfiACPIMemoryNVS
+ );
+ }
#endif
}

--
2.27.0


[PATCH v12 29/46] OvmfPkg: Create a GHCB page for use during Sec phase

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

A GHCB page is needed during the Sec phase, so this new page must be
created. Since the #VC exception handler routines assume that a per-CPU
variable area is immediately after the GHCB, this per-CPU variable area
must also be created. Since the GHCB must be marked as an un-encrypted,
or shared, page, an additional pagetable page is required to break down
the 2MB region where the GHCB page lives into 4K pagetable entries.

Create a new entry in the OVMF memory layout for the new page table
page and for the SEC GHCB and per-CPU variable pages. After breaking down
the 2MB page, update the GHCB page table entry to remove the encryption
mask.

The GHCB page will be used by the SEC #VC exception handler. The #VC
exception handler will fill in the necessary fields of the GHCB and exit
to the hypervisor using the VMGEXIT instruction. The hypervisor then
accesses the GHCB in order to perform the requested function.

Four new fixed PCDs are needed to support the SEC GHCB page:
- PcdOvmfSecGhcbBase UINT32 value that is the base address of the
GHCB used during the SEC phase.
- PcdOvmfSecGhcbSize UINT32 value that is the size, in bytes, of the
GHCB area used during the SEC phase.

- PcdOvmfSecGhcbPageTableBase UINT32 value that is address of a page
table page used to break down the 2MB page into
512 4K pages.
- PcdOvmfSecGhcbPageTableSize UINT32 value that is the size, in bytes,
of the page table page.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/OvmfPkg.dec | 9 +++
OvmfPkg/OvmfPkgX64.fdf | 6 ++
OvmfPkg/ResetVector/ResetVector.inf | 5 ++
OvmfPkg/ResetVector/Ia32/PageTables64.asm | 76 +++++++++++++++++++++++
OvmfPkg/ResetVector/ResetVector.nasmb | 17 +++++
5 files changed, 113 insertions(+)

diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index f16c00ad5b99..74d88f61617c 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -289,6 +289,15 @@ [PcdsFixedAtBuild]
## Number of page frames to use for storing grant table entries.
gUefiOvmfPkgTokenSpaceGuid.PcdXenGrantFrames|4|UINT32|0x33

+ ## Specify the extra page table needed to mark the GHCB as unencrypted.
+ # The value should be a multiple of 4KB for each.
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|0x0|UINT32|0x3e
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize|0x0|UINT32|0x3f
+
+ ## The base address of the SEC GHCB page used by SEV-ES.
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|0|UINT32|0x40
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize|0|UINT32|0x41
+
[PcdsDynamic, PcdsDynamicEx]
gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 83ff6aef2e8c..edb03b5464d4 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -76,6 +76,12 @@ [FD.MEMFD]
0x007000|0x001000
gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress|gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize

+0x008000|0x001000
+gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
+
+0x009000|0x002000
+gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
+
0x010000|0x010000
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize

diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf
index b0ddfa5832a2..483fd90fe785 100644
--- a/OvmfPkg/ResetVector/ResetVector.inf
+++ b/OvmfPkg/ResetVector/ResetVector.inf
@@ -26,6 +26,7 @@ [Sources]
[Packages]
OvmfPkg/OvmfPkg.dec
MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
UefiCpuPkg/UefiCpuPkg.dec

[BuildOptions]
@@ -33,5 +34,9 @@ [BuildOptions]
*_*_X64_NASMB_FLAGS = -I$(WORKSPACE)/UefiCpuPkg/ResetVector/Vtf0/

[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize
diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
index abad009f20f5..9f86ddf6f08f 100644
--- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
+++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
@@ -21,6 +21,11 @@ BITS 32
%define PAGE_2M_MBO 0x080
%define PAGE_2M_PAT 0x01000

+%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \
+ PAGE_DIRTY + \
+ PAGE_READ_WRITE + \
+ PAGE_PRESENT)
+
%define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \
PAGE_ACCESSED + \
PAGE_DIRTY + \
@@ -75,6 +80,37 @@ NoSev:
SevExit:
OneTimeCallRet CheckSevFeature

+; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature
+; is enabled.
+;
+; Modified: EAX, EBX, ECX
+;
+; If SEV-ES is enabled then EAX will be non-zero.
+; If SEV-ES is disabled then EAX will be zero.
+;
+CheckSevEsFeature:
+ xor eax, eax
+
+ ; SEV-ES can't be enabled if SEV isn't, so first check the encryption
+ ; mask.
+ test edx, edx
+ jz NoSevEs
+
+ ; Save current value of encryption mask
+ mov ebx, edx
+
+ ; Check if SEV-ES is enabled
+ ; MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
+ mov ecx, 0xc0010131
+ rdmsr
+ and eax, 2
+
+ ; Restore encryption mask
+ mov edx, ebx
+
+NoSevEs:
+ OneTimeCallRet CheckSevEsFeature
+
;
; Modified: EAX, EBX, ECX, EDX
;
@@ -139,6 +175,46 @@ pageTableEntriesLoop:
mov [(ecx * 8 + PT_ADDR (0x2000 - 8)) + 4], edx
loop pageTableEntriesLoop

+ OneTimeCall CheckSevEsFeature
+ test eax, eax
+ jz SetCr3
+
+ ;
+ ; The initial GHCB will live at GHCB_BASE and needs to be un-encrypted.
+ ; This requires the 2MB page for this range be broken down into 512 4KB
+ ; pages. All will be marked encrypted, except for the GHCB.
+ ;
+ mov ecx, (GHCB_BASE >> 21)
+ mov eax, GHCB_PT_ADDR + PAGE_PDP_ATTR
+ mov [ecx * 8 + PT_ADDR (0x2000)], eax
+
+ ;
+ ; Page Table Entries (512 * 4KB entries => 2MB)
+ ;
+ mov ecx, 512
+pageTableEntries4kLoop:
+ mov eax, ecx
+ dec eax
+ shl eax, 12
+ add eax, GHCB_BASE & 0xFFE0_0000
+ add eax, PAGE_4K_PDE_ATTR
+ mov [ecx * 8 + GHCB_PT_ADDR - 8], eax
+ mov [(ecx * 8 + GHCB_PT_ADDR - 8) + 4], edx
+ loop pageTableEntries4kLoop
+
+ ;
+ ; Clear the encryption bit from the GHCB entry
+ ;
+ mov ecx, (GHCB_BASE & 0x1F_FFFF) >> 12
+ mov [ecx * 8 + GHCB_PT_ADDR + 4], strict dword 0
+
+ mov ecx, GHCB_SIZE / 4
+ xor eax, eax
+clearGhcbMemoryLoop:
+ mov dword[ecx * 4 + GHCB_BASE - 4], eax
+ loop clearGhcbMemoryLoop
+
+SetCr3:
;
; Set CR3 now that the paging structures are available
;
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 75cfe16654b1..bfb77e439105 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -53,8 +53,25 @@
%error "This implementation inherently depends on PcdOvmfSecPageTablesSize"
%endif

+ %if (FixedPcdGet32 (PcdOvmfSecGhcbPageTableSize) != 0x1000)
+ %error "This implementation inherently depends on PcdOvmfSecGhcbPageTableSize"
+ %endif
+
+ %if (FixedPcdGet32 (PcdOvmfSecGhcbSize) != 0x2000)
+ %error "This implementation inherently depends on PcdOvmfSecGhcbSize"
+ %endif
+
+ %if ((FixedPcdGet32 (PcdOvmfSecGhcbBase) >> 21) != \
+ ((FixedPcdGet32 (PcdOvmfSecGhcbBase) + FixedPcdGet32 (PcdOvmfSecGhcbSize) - 1) >> 21))
+ %error "This implementation inherently depends on PcdOvmfSecGhcbBase not straddling a 2MB boundary"
+ %endif
+
%define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
%include "Ia32/Flat32ToFlat64.asm"
+
+ %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase))
+ %define GHCB_BASE (FixedPcdGet32 (PcdOvmfSecGhcbBase))
+ %define GHCB_SIZE (FixedPcdGet32 (PcdOvmfSecGhcbSize))
%include "Ia32/PageTables64.asm"
%endif

--
2.27.0


[PATCH v12 28/46] OvmfPkg: Add support to perform SEV-ES initialization

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

When SEV-ES is enabled, then SEV is also enabled. Add support to the SEV
initialization function to also check for SEV-ES being enabled, and if
enabled, set the SEV-ES enabled PCD (PcdSevEsIsEnabled).

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/OvmfPkgIa32.dsc | 3 +++
OvmfPkg/OvmfPkgIa32X64.dsc | 3 +++
OvmfPkg/OvmfPkgX64.dsc | 3 +++
OvmfPkg/PlatformPei/PlatformPei.inf | 1 +
OvmfPkg/PlatformPei/AmdSev.c | 26 ++++++++++++++++++++++++++
5 files changed, 36 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index c57bba1ba197..f84f23f250ef 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -607,6 +607,9 @@ [PcdsDynamicDefault]
# Set memory encryption mask
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0

+ # Set SEV-ES defaults
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
+
!if $(SMM_REQUIRE) == TRUE
gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes|8
gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 22e930b12b9b..a66abccf8266 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -619,6 +619,9 @@ [PcdsDynamicDefault]
# Set memory encryption mask
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0

+ # Set SEV-ES defaults
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
+
!if $(SMM_REQUIRE) == TRUE
gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes|8
gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 60be5eae3d2b..2a8975fd3d29 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -617,6 +617,9 @@ [PcdsDynamicDefault]
# Set memory encryption mask
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask|0x0

+ # Set SEV-ES defaults
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled|0
+
!if $(SMM_REQUIRE) == TRUE
gUefiOvmfPkgTokenSpaceGuid.PcdQ35TsegMbytes|8
gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE
diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index ff397b3ee9d7..00feb96c9308 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -103,6 +103,7 @@ [Pcd]
gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
gUefiCpuPkgTokenSpaceGuid.PcdCpuBootLogicalProcessorNumber
gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize
+ gUefiCpuPkgTokenSpaceGuid.PcdSevEsIsEnabled

[FixedPcd]
gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
diff --git a/OvmfPkg/PlatformPei/AmdSev.c b/OvmfPkg/PlatformPei/AmdSev.c
index e484f4b311fe..4dc5340caa7a 100644
--- a/OvmfPkg/PlatformPei/AmdSev.c
+++ b/OvmfPkg/PlatformPei/AmdSev.c
@@ -21,6 +21,27 @@

#include "Platform.h"

+/**
+
+ Initialize SEV-ES support if running as an SEV-ES guest.
+
+ **/
+STATIC
+VOID
+AmdSevEsInitialize (
+ VOID
+ )
+{
+ RETURN_STATUS PcdStatus;
+
+ if (!MemEncryptSevEsIsEnabled ()) {
+ return;
+ }
+
+ PcdStatus = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
+ ASSERT_RETURN_ERROR (PcdStatus);
+}
+
/**

Function checks if SEV support is available, if present then it sets
@@ -103,4 +124,9 @@ AmdSevInitialize (
);
}
}
+
+ //
+ // Check and perform SEV-ES initialization if required.
+ //
+ AmdSevEsInitialize ();
}
--
2.27.0


[PATCH v12 27/46] OvmfPkg/MemEncryptSevLib: Add an SEV-ES guest indicator function

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Create a function that can be used to determine if the VM is running
as an SEV-ES guest.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/Include/Library/MemEncryptSevLib.h | 12 +++
.../MemEncryptSevLibInternal.c | 75 ++++++++++++-------
2 files changed, 60 insertions(+), 27 deletions(-)

diff --git a/OvmfPkg/Include/Library/MemEncryptSevLib.h b/OvmfPkg/Include/Library/MemEncryptSevLib.h
index 64dd6977b0f8..a50a0de9c870 100644
--- a/OvmfPkg/Include/Library/MemEncryptSevLib.h
+++ b/OvmfPkg/Include/Library/MemEncryptSevLib.h
@@ -13,6 +13,18 @@

#include <Base.h>

+/**
+ Returns a boolean to indicate whether SEV-ES is enabled
+
+ @retval TRUE SEV-ES is enabled
+ @retval FALSE SEV-ES is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevEsIsEnabled (
+ VOID
+ );
+
/**
Returns a boolean to indicate whether SEV is enabled

diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c b/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c
index 96a66e373f11..3301c5c2862f 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/MemEncryptSevLibInternal.c
@@ -20,19 +20,17 @@
#include <Uefi/UefiBaseType.h>

STATIC BOOLEAN mSevStatus = FALSE;
+STATIC BOOLEAN mSevEsStatus = FALSE;
STATIC BOOLEAN mSevStatusChecked = FALSE;

/**
+ Reads and sets the status of SEV features

- Returns a boolean to indicate whether SEV is enabled
-
- @retval TRUE SEV is enabled
- @retval FALSE SEV is not enabled
**/
STATIC
-BOOLEAN
+VOID
EFIAPI
-InternalMemEncryptSevIsEnabled (
+InternalMemEncryptSevStatus (
VOID
)
{
@@ -56,32 +54,55 @@ InternalMemEncryptSevIsEnabled (
//
Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);
if (Msr.Bits.SevBit) {
- return TRUE;
+ mSevStatus = TRUE;
+ }
+
+ //
+ // Check MSR_0xC0010131 Bit 1 (Sev-Es Enabled)
+ //
+ if (Msr.Bits.SevEsBit) {
+ mSevEsStatus = TRUE;
}
}
}

- return FALSE;
-}
-
-/**
- Returns a boolean to indicate whether SEV is enabled
-
- @retval TRUE SEV is enabled
- @retval FALSE SEV is not enabled
-**/
-BOOLEAN
-EFIAPI
-MemEncryptSevIsEnabled (
- VOID
- )
-{
- if (mSevStatusChecked) {
- return mSevStatus;
- }
-
- mSevStatus = InternalMemEncryptSevIsEnabled();
mSevStatusChecked = TRUE;
+}
+
+/**
+ Returns a boolean to indicate whether SEV-ES is enabled
+
+ @retval TRUE SEV-ES is enabled
+ @retval FALSE SEV-ES is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevEsIsEnabled (
+ VOID
+ )
+{
+ if (!mSevStatusChecked) {
+ InternalMemEncryptSevStatus ();
+ }
+
+ return mSevEsStatus;
+}
+
+/**
+ Returns a boolean to indicate whether SEV is enabled
+
+ @retval TRUE SEV is enabled
+ @retval FALSE SEV is not enabled
+**/
+BOOLEAN
+EFIAPI
+MemEncryptSevIsEnabled (
+ VOID
+ )
+{
+ if (!mSevStatusChecked) {
+ InternalMemEncryptSevStatus ();
+ }

return mSevStatus;
}
--
2.27.0


[PATCH v12 26/46] OvmfPkg/VmgExitLib: Add support for DR7 Read/Write NAE events

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Under SEV-ES, a DR7 read or write intercept generates a #VC exception.
The #VC handler must provide special support to the guest for this. On
a DR7 write, the #VC handler must cache the value and issue a VMGEXIT
to notify the hypervisor of the write. However, the #VC handler must
not actually set the value of the DR7 register. On a DR7 read, the #VC
handler must return the cached value of the DR7 register to the guest.
VMGEXIT is not invoked for a DR7 register read.

The caching of the DR7 values will make use of the per-CPU data pages
that are allocated along with the GHCB pages. The per-CPU page for a
vCPU is the page that immediately follows the vCPU's GHCB page. Since
each GHCB page is unique for a vCPU, the page that follows becomes
unique for that vCPU. The SEC phase will reserves an area of memory for
a single GHCB and per-CPU page for use by the BSP. After transitioning
to the PEI phase, new GHCB and per-CPU pages are allocated for the BSP
and all APs.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Acked-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c | 114 ++++++++++++++++++
1 file changed, 114 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
index e70e0ef82f68..c57c8c4ba203 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
@@ -126,6 +126,14 @@ UINT64
SEV_ES_INSTRUCTION_DATA *InstructionData
);

+//
+// Per-CPU data mapping structure
+//
+typedef struct {
+ BOOLEAN Dr7Cached;
+ UINT64 Dr7;
+} SEV_ES_PER_CPU_DATA;
+

/**
Checks the GHCB to determine if the specified register has been marked valid.
@@ -1480,6 +1488,104 @@ RdtscExit (
return 0;
}

+/**
+ Handle a DR7 register write event.
+
+ Use the VMGEXIT instruction to handle a DR7 write event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+Dr7WriteExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ SEV_ES_PER_CPU_DATA *SevEsData;
+ UINT64 *Register;
+ UINT64 Status;
+
+ Ext = &InstructionData->Ext;
+ SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
+
+ DecodeModRm (Regs, InstructionData);
+
+ //
+ // MOV DRn always treats MOD == 3 no matter how encoded
+ //
+ Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
+
+ //
+ // Using a value of 0 for ExitInfo1 means RAX holds the value
+ //
+ Ghcb->SaveArea.Rax = *Register;
+ GhcbSetRegValid (Ghcb, GhcbRax);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ SevEsData->Dr7 = *Register;
+ SevEsData->Dr7Cached = TRUE;
+
+ return 0;
+}
+
+/**
+ Handle a DR7 register read event.
+
+ Use the VMGEXIT instruction to handle a DR7 read event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+
+**/
+STATIC
+UINT64
+Dr7ReadExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ SEV_ES_PER_CPU_DATA *SevEsData;
+ UINT64 *Register;
+
+ Ext = &InstructionData->Ext;
+ SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
+
+ DecodeModRm (Regs, InstructionData);
+
+ //
+ // MOV DRn always treats MOD == 3 no matter how encoded
+ //
+ Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
+
+ //
+ // If there is a cached valued for DR7, return that. Otherwise return the
+ // DR7 standard reset value of 0x400 (no debug breakpoints set).
+ //
+ *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : 0x400;
+
+ return 0;
+}
+
/**
Handle a #VC exception.

@@ -1524,6 +1630,14 @@ VmgExitHandleVc (

ExitCode = Regs->ExceptionData;
switch (ExitCode) {
+ case SVM_EXIT_DR7_READ:
+ NaeExit = Dr7ReadExit;
+ break;
+
+ case SVM_EXIT_DR7_WRITE:
+ NaeExit = Dr7WriteExit;
+ break;
+
case SVM_EXIT_RDTSC:
NaeExit = RdtscExit;
break;
--
2.27.0


[PATCH v12 25/46] OvmfPkg/VmgExitLib: Add support for MWAIT/MWAITX NAE events

Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198

Under SEV-ES, a MWAIT/MWAITX intercept generates a #VC exception.
VMGEXIT must be used to allow the hypervisor to handle this intercept.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Acked-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)

diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
index fe08b1e0ff49..e70e0ef82f68 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
@@ -856,6 +856,38 @@ MmioExit (
return Status;
}

+/**
+ Handle a MWAIT event.
+
+ Use the VMGEXIT instruction to handle a MWAIT event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MwaitExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ DecodeModRm (Regs, InstructionData);
+
+ Ghcb->SaveArea.Rax = Regs->Rax;
+ GhcbSetRegValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ GhcbSetRegValid (Ghcb, GhcbRcx);
+
+ return VmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);
+}
+
/**
Handle a MONITOR event.

@@ -1532,6 +1564,10 @@ VmgExitHandleVc (
NaeExit = MonitorExit;
break;

+ case SVM_EXIT_MWAIT:
+ NaeExit = MwaitExit;
+ break;
+
case SVM_EXIT_NPF:
NaeExit = MmioExit;
break;
--
2.27.0