Date   

Re: RFC: Fast Migration for SEV and SEV-ES - blueprint and proof of concept

Ashish Kalra <ashish.kalra@...>
 

Hello Tobin,

On Wed, Oct 28, 2020 at 03:31:44PM -0400, Tobin Feldman-Fitzthum wrote:
Hello,

Dov Murik. James Bottomley, Hubertus Franke, and I have been working on a
plan for fast live migration of SEV and SEV-ES (and SEV-SNP when it's out
and even hopefully Intel TDX) VMs. We have developed an approach that we
believe is feasible and a demonstration that shows our solution to the most
difficult part of the problem. In short, we have implemented a UEFI
Application that can resume from a VM snapshot. We think this is the crux of
SEV-ES live migration. After describing the context of our demo and how it
works, we explain how it can be extended to a full SEV-ES migration. Our
goal is to show that fast SEV and SEV-ES live migration can be implemented
in OVMF with minimal kernel changes. We provide a blueprint for doing so.

Typically the hypervisor facilitates live migration. AMD SEV excludes the
hypervisor from the trust domain of the guest. When a hypervisor (HV)
examines the memory of an SEV guest, it will find only a ciphertext. If the
HV moves the memory of an SEV guest, the ciphertext will be invalidated.
Furthermore, with SEV-ES the hypervisor is largely unable to access guest
CPU state. Thus, fast migration of SEV VMs requires support from inside the
trust domain, i.e. the guest.

One approach is to add support for SEV Migration to the Linux kernel. This
would allow the guest to encrypt/decrypt its own memory with a transport
key. This approach has met some resistance. We propose a similar approach
implemented not in Linux, but in firmware, specifically OVMF. Since OVMF
runs inside the guest, it has access to the guest memory and CPU state. OVMF
should be able to perform the manipulations required for live migration of
SEV and SEV-ES guests.

The biggest challenge of this approach involves migrating the CPU state of
an SEV-ES guest. In a normal (non-SEV migration) the HV sets the CPU state
of the target before the target begins executing. In our approach, the HV
starts the target and OVMF must resume to whatever state the source was in.
We believe this to be the crux (or at least the most difficult part) of live
migration for SEV and we hope that by demonstrating resume from EFI, we can
show that our approach is generally feasible.

Our demo can be found at <https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fsecure-migration&;data=04%7C01%7Cashish.kalra%40amd.com%7C6edb93f8936e465a9fee08d87b781d00%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637395103097650163%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=dsOh3zcwSWgnpmMdcCnSoJ%2B3Ohqz175axch%2B%2Bnu73Uc%3D&amp;reserved=0>.
The tooling repository is the best starting point. It contains documentation
about the project and the scripts needed to run the demo. There are two more
repos associated with the project. One is a modified edk2 tree that contains
our modified OVMF. The other is a modified qemu, that has a couple of
temporary changes needed for the demo. Our demonstration is aimed only at
resuming from a VM snapshot in OVMF. We provide the source CPU state and
source memory to the destination using temporary plumbing that violates the
SEV trust model. We explain the setup in more depth in README.md. We are
showing only that OVMF can resume from a VM snapshot. At the end we will
describe our plan for transferring CPU state and memory from source to
guest. To be clear, the temporary tooling used for this demo isn't built for
encrypted VMs, but below we explain how this demo applies to and can be
extended to encrypted VMs.

We Implemented our resume code in a very similar fashion to the recommended
S3 resume code. When the HV sets the CPU state of a guest, it can do so when
the guest is not executing. Setting the state from inside the guest is a
delicate operation. There is no way to atomically set all of the CPU state
from inside the guest. Instead, we must set most registers individually and
account for changes in control flow that doing so might cause. We do this
with a three-phase trampoline. OVMF calls phase 1, which runs on the OVMF
map. Phase 1 sets up phase 2 and jumps to it. Phase 2 switches to an
intermediate map that reconciles the OVMF map and the source map. Phase 3
switches to the source map, restores the registers, and returns into
execution of the source. We will go backwards through these phases in more
depth.

The last thing that resume to EFI does is return. Specifically, we use
IRETQ, which reads the values of RIP, CS, RFLAGS, RSP, and SS from a
temporary stack and restores them atomically, thus returning to source
execution. Prior to returning, we must manually restore most other registers
to the values they had on the source. One particularly significant register
is CR3. When we return to Linux, CR3 must be set to the source CR3 or the
first instruction executed in Linux will cause a page fault. The code that
we use to restore the registers and return must be mapped in the source page
table or we would get a page fault executing the instructions prior to
returning into Linux. The value of CR3 is so significant, that it defines
the three phases of the trampoline. Phase 3 begins when CR3 is set to the
source CR3. After setting CR3, we set all the other registers and return.

Phase 2 mainly exists to setup phase 3. OVMF uses a 1-1 mapping, meaning
that virtual addresses are the same as physical addresses. The kernel page
table uses an offset mapping, meaning that virtual addresses differ from
physical addresses by a constant (for the most part). Crucially, this means
that the virtual address of the page that is executed by phase 3 differs
between the OVMF map and the source map. If we are executing code mapped in
OVMF and we change CR3 to point to the source map, although the page may be
mapped in the source map, the virtual address will be different, and we will
face undefined behavior. To fix this, we construct intermediate page tables
that map the pages for phase 2 and 3 to the virtual address expected in OVMF
and to the virtual address expected in the source map. Thus, we can switch
CR3 from OVMF's map to the intermediate map and then from the intermediate
map to the source map. Phase 2 is much shorter than phase 3. Phase 2 is
mainly responsible for switching to the intermediate map, flushing the TLB,
and jumping to phase 3.

Fortunately phase 1 is even simpler than phase 2. Phase 1 has two duties.
First, since phase 2 and 3 operate without a stack and can't access values
defined in OVMF (such as the addresses of the pages containing phase 2 and
3), phase 1 must pass these values to phase 2 by putting them in registers.
Second, phase 1 must start phase 2 by jumping to it.

Given that we can resume to a snapshot in OVMF, we should be able to migrate
an SEV guest as long as we can securely communicate the VM snapshot from
source to destination. For our demo, we do this with a handful of QMP
commands. More sophisticated methods are required for a production
implementation.

When we refer to a snapshot, what we really mean is the device state,
memory, and CPU state of a guest. In live migration this is transmitted
dynamically as opposed to being saved and restored. Device state is not
protected by SEV and can be handled entirely by the HV. Memory, on the other
hand, cannot be handled only by the HV. As mentioned previously, memory
needs to be encrypted with a transport key. A Migration Handler on the
source will coordinate with the HV to encrypt pages and transmit them to the
destination. The destination HV will receive the pages over the network and
pass them to the Migration Handler in the target VM so they can be
decrypted. This transmission will occur continuously until the memory of the
source and target converges.

Plain SEV does not protect the CPU state of the guest and therefore does not
require any special mechanism for transmission of the CPU state. We plan to
implement an end-to-end migration with plain SEV first. In SEV-ES, the PSP
(platform security processor) encrypts CPU state on each VMExit. The
encrypted state is stored in memory. Normally this memory (known as the
VMSA) is not mapped into the guest, but we can add an entry to the nested
page tables that will expose the VMSA to the guest.
I have a question here, is there any kind of integrity protection on the
CPU state when the target VM is resumed after nigration, for example, if
there is a malicious hypervisor which maps a page with subverted CPU
state on the nested page tables, what prevents the target VM to resume
execution on a subverted or compromised CPU state ?

Thanks,
Ashish

This means that when the
guest VMExits, the CPU state will be saved to guest memory. With the CPU
state in guest memory, it can be transmitted to the target using the method
described above.

In addition to the changes needed in OVMF to resume the VM, the transmission
of the VM from source to target will require a new code path in the
hypervisor. There will also need to be a few minor changes to Linux (adding
a mapping for our Phase 3 pages). Despite all the moving pieces, we believe
that this is a feasible approach for supporting live migration for SEV and
SEV-ES.

For the sake of brevity, we have left out a few issues, including SMP
support, generation of the intermediate mappings, and more. We have included
some notes about these issues in the COMPLICATIONS.md file. We also have an
outline of an end-to-end implementation of live migration for SEV-ES in
END-TO-END.md. See README.md for info on how to run the demo. While this is
not a full migration, we hope to show that fast live migration with SEV and
SEV-ES is possible without major kernel changes.

-Tobin


[PATCH v3 1/1] Silicon/Qemu/Sbsa: Add SBSA-wdt entry to GTDT

Shashi Mallela
 

SBSA generic watchdog timer structure entry has been added
to GTDT table as per SBSAv6.0.
This enables acpi detection of wdt in qemu sbsa platform

Cc: Leif Lindholm <leif@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Cc: Graeme Gregory <graeme@...>
Signed-off-by: Shashi Mallela <shashi.mallela@...>
---
Silicon/Qemu/SbsaQemu/AcpiTables/Gtdt.aslc | 87 ++++++++++++++------
1 file changed, 62 insertions(+), 25 deletions(-)

diff --git a/Silicon/Qemu/SbsaQemu/AcpiTables/Gtdt.aslc b/Silicon/Qemu/SbsaQemu/AcpiTables/Gtdt.aslc
index d16778e01a5c..1713c203766e 100644
--- a/Silicon/Qemu/SbsaQemu/AcpiTables/Gtdt.aslc
+++ b/Silicon/Qemu/SbsaQemu/AcpiTables/Gtdt.aslc
@@ -24,42 +24,79 @@
#define SYSTEM_TIMER_BASE_ADDRESS 0xFFFFFFFFFFFFFFFF
#endif

-#define GTDT_TIMER_EDGE_TRIGGERED EFI_ACPI_5_0_GTDT_TIMER_FLAG_TIMER_INTERRUPT_MODE
+#define GTDT_TIMER_EDGE_TRIGGERED EFI_ACPI_6_3_GTDT_TIMER_FLAG_TIMER_INTERRUPT_MODE
#define GTDT_TIMER_LEVEL_TRIGGERED 0
-#define GTDT_TIMER_ACTIVE_LOW EFI_ACPI_5_0_GTDT_TIMER_FLAG_TIMER_INTERRUPT_POLARITY
+#define GTDT_TIMER_ACTIVE_LOW EFI_ACPI_6_3_GTDT_TIMER_FLAG_TIMER_INTERRUPT_POLARITY
#define GTDT_TIMER_ACTIVE_HIGH 0

#define GTDT_GTIMER_FLAGS (GTDT_TIMER_ACTIVE_LOW | GTDT_TIMER_LEVEL_TRIGGERED)

+#define SBSA_PLATFORM_WATCHDOG_COUNT 1
+#define SBSA_PLATFORM_TIMER_COUNT (SBSA_PLATFORM_WATCHDOG_COUNT)
+
+#define SBSAQEMU_WDT_REFRESH_FRAME_BASE 0x50010000
+#define SBSAQEMU_WDT_CONTROL_FRAME_BASE 0x50011000
+#define SBSAQEMU_WDT_IRQ 44
+
+#define GTDT_WDTIMER_EDGE_TRIGGERED EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_FLAG_TIMER_INTERRUPT_MODE
+#define GTDT_WDTIMER_LEVEL_TRIGGERED 0
+#define GTDT_WDTIMER_ACTIVE_LOW EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_FLAG_TIMER_INTERRUPT_POLARITY
+#define GTDT_WDTIMER_ACTIVE_HIGH 0
+
+#define GTDT_WDTIMER_FLAGS (GTDT_WDTIMER_ACTIVE_HIGH | GTDT_WDTIMER_LEVEL_TRIGGERED)
+
+#define EFI_ACPI_6_3_SBSA_GENERIC_WATCHDOG_STRUCTURE_INIT( \
+ RefreshFramePhysicalAddress, ControlFramePhysicalAddress, \
+ WatchdogTimerGSIV, WatchdogTimerFlags) \
+ { \
+ EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG, \
+ sizeof(EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE), \
+ EFI_ACPI_RESERVED_WORD, \
+ RefreshFramePhysicalAddress, \
+ ControlFramePhysicalAddress, \
+ WatchdogTimerGSIV, \
+ WatchdogTimerFlags \
+ }
+
#pragma pack (1)

typedef struct {
- EFI_ACPI_5_1_GENERIC_TIMER_DESCRIPTION_TABLE Gtdt;
- } GENERIC_TIMER_DESCRIPTION_TABLE;
+ EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE Gtdt;
+ EFI_ACPI_6_3_GTDT_SBSA_GENERIC_WATCHDOG_STRUCTURE Gwdt;
+ } GENERIC_TIMER_DESCRIPTION_TABLES;

#pragma pack ()

- GENERIC_TIMER_DESCRIPTION_TABLE Gtdt = {
- {
- SBSAQEMU_ACPI_HEADER(
- EFI_ACPI_5_1_GENERIC_TIMER_DESCRIPTION_TABLE_SIGNATURE,
- GENERIC_TIMER_DESCRIPTION_TABLE,
- EFI_ACPI_5_1_GENERIC_TIMER_DESCRIPTION_TABLE_REVISION
- ),
- SYSTEM_TIMER_BASE_ADDRESS, // UINT64 PhysicalAddress
- 0, // UINT32 Reserved
- FixedPcdGet32 (PcdArmArchTimerSecIntrNum), // UINT32 SecurePL1TimerGSIV
- GTDT_GTIMER_FLAGS, // UINT32 SecurePL1TimerFlags
- FixedPcdGet32 (PcdArmArchTimerIntrNum), // UINT32 NonSecurePL1TimerGSIV
- GTDT_GTIMER_FLAGS, // UINT32 NonSecurePL1TimerFlags
- FixedPcdGet32 (PcdArmArchTimerVirtIntrNum), // UINT32 VirtualTimerGSIV
- GTDT_GTIMER_FLAGS, // UINT32 VirtualTimerFlags
- FixedPcdGet32 (PcdArmArchTimerHypIntrNum), // UINT32 NonSecurePL2TimerGSIV
- GTDT_GTIMER_FLAGS, // UINT32 NonSecurePL2TimerFlags
- 0xFFFFFFFFFFFFFFFF, // UINT64 CntReadBasePhysicalAddress
- 0, // UINT32 PlatformTimerCount
- 0
- },
+ GENERIC_TIMER_DESCRIPTION_TABLES Gtdt = {
+ {
+ SBSAQEMU_ACPI_HEADER(
+ EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE_SIGNATURE,
+ GENERIC_TIMER_DESCRIPTION_TABLES,
+ EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE_REVISION
+ ),
+ SYSTEM_TIMER_BASE_ADDRESS, // UINT64 PhysicalAddress
+ 0, // UINT32 Reserved
+ FixedPcdGet32 (PcdArmArchTimerSecIntrNum), // UINT32 SecurePL1TimerGSIV
+ GTDT_GTIMER_FLAGS, // UINT32 SecurePL1TimerFlags
+ FixedPcdGet32 (PcdArmArchTimerIntrNum), // UINT32 NonSecurePL1TimerGSIV
+ GTDT_GTIMER_FLAGS, // UINT32 NonSecurePL1TimerFlags
+ FixedPcdGet32 (PcdArmArchTimerVirtIntrNum), // UINT32 VirtualTimerGSIV
+ GTDT_GTIMER_FLAGS, // UINT32 VirtualTimerFlags
+ FixedPcdGet32 (PcdArmArchTimerHypIntrNum), // UINT32 NonSecurePL2TimerGSIV
+ GTDT_GTIMER_FLAGS, // UINT32 NonSecurePL2TimerFlags
+ 0xFFFFFFFFFFFFFFFF, // UINT64 CntReadBasePhysicalAddress
+ SBSA_PLATFORM_TIMER_COUNT, // UINT32 PlatformTimerCount
+ sizeof(EFI_ACPI_6_3_GENERIC_TIMER_DESCRIPTION_TABLE),
+ // UINT32 PlatformTimerOffset
+ 0, // UINT32 VirtualPL2TimerGSIV
+ 0 // UINT32 VirtualPL2TimerFlags
+ },
+ EFI_ACPI_6_3_SBSA_GENERIC_WATCHDOG_STRUCTURE_INIT(
+ SBSAQEMU_WDT_REFRESH_FRAME_BASE,
+ SBSAQEMU_WDT_CONTROL_FRAME_BASE,
+ SBSAQEMU_WDT_IRQ,
+ GTDT_WDTIMER_FLAGS
+ )
};

// Reference the table being generated to prevent the optimizer from removing the
--
2.18.4


[PATCH v3 0/1] Add SBSA-wdt entry to GTDT

Shashi Mallela
 

To enable detection of qemu SBSA generic watchdog timer device,this
patch has been added to create sbsa-wdt entry into the GTDT table
which helps firmware report the presence of SBSA-wdt to the OS.

Changes in v3:
- fixed typo from earlier patch which was resulting in build failure
- confirmed the overall functionality using latest build and
testing with related qemu code for sbsa wdt

Shashi Mallela (1):
Silicon/Qemu/Sbsa: Add SBSA-wdt entry to GTDT

Silicon/Qemu/SbsaQemu/AcpiTables/Gtdt.aslc | 87 ++++++++++++++------
1 file changed, 62 insertions(+), 25 deletions(-)

--
2.18.4


[PATCH 1/1] uefi-sct/SctPkg: NULL deref in DevicePathToText test

Heinrich Schuchardt
 

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3D3029

Function DevicePathToTextConvertDeviceNodeToTextCoverageTest() tests if
DeviceNodeToText() correctly converts a Relative Offset Range node. After
calling SctConvertTextToDeviceNode() it tries to set the field Reserved
of the returned device node to 0.

If the tested firmware does not return the expected text
SctConvertTextToDeviceNode() may return NULL or a device node that is
shorter than expected. In both cases it is not possible to access the
field Reserved.

So we must check both that the returned node is not NULL and that it has
the exepected size.

Due to the missing check a NULL dereference was observed when running the
SCT on U-Boot.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@...>
---
.../BlackBoxTest/DevicePathToTextBBTestCoverage.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/uefi-sct/SctPkg/TestCase/UEFI/EFI/Protocol/DevicePathToText/Bl=
ackBoxTest/DevicePathToTextBBTestCoverage.c b/uefi-sct/SctPkg/TestCase/UEFI=
/EFI/Protocol/DevicePathToText/BlackBoxTest/DevicePathToTextBBTestCoverage.c
index ee91bdfb..784d4748 100644
--- a/uefi-sct/SctPkg/TestCase/UEFI/EFI/Protocol/DevicePathToText/BlackBoxT=
est/DevicePathToTextBBTestCoverage.c
+++ b/uefi-sct/SctPkg/TestCase/UEFI/EFI/Protocol/DevicePathToText/BlackBoxT=
est/DevicePathToTextBBTestCoverage.c
@@ -1198,8 +1198,12 @@ DevicePathToTextConvertDeviceNodeToTextCoverageTest (
((MEDIA_OFFSET_DEVICE_PATH *)pDeviceNode1)->EndingOffset =3D 0x1234;=0D
Text =3D DevicePathToText->ConvertDeviceNodeToText (pDeviceNode1, FALSE,=
FALSE);=0D
pDeviceNode2 =3D SctConvertTextToDeviceNode(Text);=0D
- ((MEDIA_OFFSET_DEVICE_PATH *)pDeviceNode2)->Reserved =3D 0;=0D
-=0D
+ SctPrint(L"pDeviceNode2 =3D %p\n", pDeviceNode2);=0D
+ if (pDeviceNode2 &&=0D
+ ((MEDIA_OFFSET_DEVICE_PATH *)pDeviceNode2)->Length =3D=3D=0D
+ sizeof(MEDIA_OFFSET_DEVICE_PATH)) {=0D
+ ((MEDIA_OFFSET_DEVICE_PATH *)pDeviceNode2)->Reserved =3D 0;=0D
+ }=0D
if ((pDeviceNode2 !=3D NULL) && (SctCompareMem (pDeviceNode2, pDeviceNod=
e1, SctDevicePathNodeLength(pDeviceNode1)) =3D=3D 0)) {=0D
AssertionType =3D EFI_TEST_ASSERTION_PASSED;=0D
} else {=0D
--=20
2.28.0


Why is MAX_LONG_FILE_PATH 500 vs. PATH_MAX?

Andrew Fish
 

Does anyone know why MAX_LONG_FILE_PATH [1] is hard coded to 500 vs. using the systems PATH_MAX?

On Linux I think PATH_MAX is 4KiB and on macOS it is 1KiB.



Thanks,

Andrew Fish


[edk2-test]

Arpan Mukherjee <arpanbzs@...>
 

Not able to build SctPkg using UDK2017 for X64 architecture in Linux(ubuntu). In SctPkg/build.sh. Showing COMPONENT_TYPE not given. Here is the log.

. SctPkg/build.sh X64 GCC
Target: X64
Build: other
Unsupported target architecture 'X64'!
TOOLCHAIN is GCC49
Toolchain prefix: GCC49_X64_PREFIX=
Building from: /home/xilinx/arpanm/edk2-vUDK2017
using prebuilt tools
Building GenBin
make: Entering directory '/home/xilinx/arpanm/edk2-vUDK2017/SctPkg/Tools/Source/GenBin'
Attempting to detect ARCH from 'uname -m': x86_64
Detected ARCH of X64 using uname.
make: Warning: File '/home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C/Makefiles/footer.makefile' has modification time 40845275 s in the future
gcc  -c -MD -fshort-wchar -fno-strict-aliasing -Wall -Werror -Wno-deprecated-declarations -Wno-unused-result -nostdlib -c -g  -I /home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C -I /home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C/Include/Common -I /home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C/Include/ -I /home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C/Include/IndustryStandard -I /home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C/Common/ -I .. -I . -I /home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C/Include/X64/  -O2 GenBin.c -o GenBin.o
gcc -o /home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C/bin/GenBin  GenBin.o -L/home/xilinx/arpanm/edk2-vUDK2017/BaseTools/Source/C/libs
make: warning:  Clock skew detected.  Your build may be incomplete.
make: Leaving directory '/home/xilinx/arpanm/edk2-vUDK2017/SctPkg/Tools/Source/GenBin'
Build environment: Linux-4.15.0-99-generic-x86_64-with-Ubuntu-16.04-xenial
Build start time: 20:01:29, Feb.26 2016

WORKSPACE        = /home/xilinx/arpanm/edk2-vUDK2017
ECP_SOURCE       = /home/xilinx/arpanm/edk2-vUDK2017/EdkCompatibilityPkg
EDK_SOURCE       = /home/xilinx/arpanm/edk2-vUDK2017/EdkCompatibilityPkg
EFI_SOURCE       = /home/xilinx/arpanm/edk2-vUDK2017
EDK_TOOLS_PATH   = /home/xilinx/arpanm/edk2-vUDK2017/BaseTools
CONF_PATH        = /home/xilinx/arpanm/edk2-vUDK2017/Conf


Architecture(s)  = X64
Build target     = DEBUG
Toolchain        = GCC49

Active Platform          = /home/xilinx/arpanm/edk2-vUDK2017/SctPkg/UEFI/UEFI_SCT.dsc

Processing meta-data ..

build.py...
/home/xilinx/arpanm/edk2-vUDK2017/SctPkg/TestCase/UEFI/EFI/BootServices/ImageServices/BlackBoxTest/Dependency/InvalidImage1/InvalidImage1.inf(...): error 5000: COMPONENT_TYPE is not given



- Failed -
Build end time: 20:01:32, Feb.26 2016
Build total time: 00:00:03

Could not build the UEFI SCT package

Please help in resolving the issue.

Thanks and regards
Arpan Mukherjee
5th-year Dual Degree student
Instrumentation and Signal Processing
Dept. of Electrical Engineering
IIT Kharagpur

Mailtrack Sender notified by
Mailtrack 10/29/20, 10:59:21 AM


Re: [PATCH 0/5] Make the MD5 disable as default setting

Feng, Roger <roger.feng@...>
 

+Qi for review

-----Original Message-----
From: Gao, Zhichao <zhichao.gao@...>
Sent: Tuesday, October 27, 2020 8:55 AM
To: Yao, Jiewen <jiewen.yao@...>; devel@edk2.groups.io
Cc: Justen, Jordan L <jordan.l.justen@...>; Laszlo Ersek <lersek@...>; Ard Biesheuvel <ard.biesheuvel@...>; Sami Mujawar <sami.mujawar@...>; Leif Lindholm <leif@...>; Wang, Jian J <jian.j.wang@...>; Lu, XiaoyuX <xiaoyux.lu@...>; Jiang, Guomin <guomin.jiang@...>; Kinney, Michael D <michael.d.kinney@...>; Steele, Kelly <kelly.steele@...>; Sun, Zailiang <zailiang.sun@...>; Qian, Yi <yi.qian@...>; Liming Gao <gaoliming@...>; Maciej Rabeda <maciej.rabeda@...>; Wu, Jiaxin <jiaxin.wu@...>; Fu, Siyuan <siyuan.fu@...>; Feng, Roger <roger.feng@...>
Subject: RE: [PATCH 0/5] Make the MD5 disable as default setting

Let me prepare the V2 to remove them(MD5 and SHA1)。

Thanks,
Zhichao

-----Original Message-----
From: Yao, Jiewen <jiewen.yao@...>
Sent: Monday, October 26, 2020 5:35 PM
To: Gao, Zhichao <zhichao.gao@...>; devel@edk2.groups.io
Cc: Justen, Jordan L <jordan.l.justen@...>; Laszlo Ersek
<lersek@...>; Ard Biesheuvel <ard.biesheuvel@...>; Sami
Mujawar <sami.mujawar@...>; Leif Lindholm <leif@...>;
Wang, Jian J <jian.j.wang@...>; Lu, XiaoyuX
<xiaoyux.lu@...>; Jiang, Guomin <guomin.jiang@...>;
Kinney, Michael D <michael.d.kinney@...>; Steele, Kelly
<kelly.steele@...>; Sun, Zailiang <zailiang.sun@...>;
Qian, Yi <yi.qian@...>; Liming Gao <gaoliming@...>;
Maciej Rabeda <maciej.rabeda@...>; Wu, Jiaxin
<jiaxin.wu@...>; Fu, Siyuan <siyuan.fu@...>; Feng, Roger
<roger.feng@...>
Subject: RE: [PATCH 0/5] Make the MD5 disable as default setting

Thanks Zhichao.

Can we remove MD5 from Hash2DxeCrypto ?
I don’t see a strong reason to include.
It should only be used by iSCSI.

Also, if possible, I prefer to remove SHA1 from Hash2DxeCrypto as well.

Thank you
Yao Jiewen


-----Original Message-----
From: Gao, Zhichao <zhichao.gao@...>
Sent: Monday, October 26, 2020 5:04 PM
To: devel@edk2.groups.io
Cc: Justen, Jordan L <jordan.l.justen@...>; Laszlo Ersek
<lersek@...>; Ard Biesheuvel <ard.biesheuvel@...>; Sami
Mujawar <sami.mujawar@...>; Leif Lindholm <leif@...>;
Yao, Jiewen <jiewen.yao@...>; Wang, Jian J
<jian.j.wang@...>; Lu, XiaoyuX <xiaoyux.lu@...>; Jiang,
Guomin <guomin.jiang@...>; Kinney, Michael D
<michael.d.kinney@...>; Steele, Kelly
<kelly.steele@...>; Sun, Zailiang <zailiang.sun@...>;
Qian, Yi <yi.qian@...>; Liming Gao <gaoliming@...>;
Maciej Rabeda <maciej.rabeda@...>; Wu, Jiaxin
<jiaxin.wu@...>; Fu, Siyuan <siyuan.fu@...>; Feng, Roger
<roger.feng@...>
Subject: [PATCH 0/5] Make the MD5 disable as default setting

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3003
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3021

MD5 is deprecated, make it disable as default for security.
It required to set MD5 enable explicitly if the module is still
using MD5. List the modules that are still using it:
iSCSI, Hash2DxeCrypto, CryptoDxe(Pei, Smm) (with PACKAGE or ALL config).

This patch set would affact the platforms that are using iSCSI
function.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Cc: Sami Mujawar <sami.mujawar@...>
Cc: Leif Lindholm <leif@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Jian J Wang <jian.j.wang@...>
Cc: Xiaoyu Lu <xiaoyux.lu@...>
Cc: Guomin Jiang <guomin.jiang@...>
Cc: Michael D Kinney <michael.d.kinney@...>
Cc: Kelly Steele <kelly.steele@...>
Cc: Zailiang Sun <zailiang.sun@...>
Cc: Yi Qian <yi.qian@...>
Cc: Liming Gao <gaoliming@...>
Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Roger Feng <roger.feng@...>
Signed-off-by: Zhichao Gao <zhichao.gao@...>

Zhichao Gao (5):
NetworkPkg/Defines: Make iSCSI disable as default
NetworkPkg: Enable MD5 while enable iSCSI
SecurityPkg/dsc: Explicitly enable MD5 for package build
CryptoPkg/dsc: Enable MD5 when CRYPTO_SERVICES enable MD5
CryptoPkg: Make the MD5 disable as default for security

CryptoPkg/CryptoPkg.dsc | 3 +++
CryptoPkg/Driver/Crypto.c | 4 ++--
CryptoPkg/Include/Library/BaseCryptLib.h | 2 +-
CryptoPkg/Library/BaseCryptLib/Hash/CryptMd5.c | 2 +-
CryptoPkg/Library/BaseCryptLibOnProtocolPpi/CryptLib.c | 2 +-
NetworkPkg/Network.dsc.inc | 5 +++++
NetworkPkg/NetworkDefines.dsc.inc | 4 ++--
SecurityPkg/SecurityPkg.dsc | 2 +-
8 files changed, 16 insertions(+), 8 deletions(-)

--
2.21.0.windows.1


Cancelled Event: TianoCore Design Meeting - APAC/NAMO - Friday, 30 October 2020 #cal-cancelled

devel@edk2.groups.io Calendar <noreply@...>
 

Cancelled: TianoCore Design Meeting - APAC/NAMO

This event has been cancelled.

When:
Friday, 30 October 2020
9:30am to 10:30am
(UTC+08:00) Asia/Chongqing

Where:
https://intel.webex.com/intel/j.php?MTID=m6bf2875d89c1ee88ca37eda8fd41a47b

Organizer: Ray Ni ray.ni@...

Description:

TOPIC

  • TBD

For more info, see here: https://www.tianocore.org/design-meeting/

Join Webex Meeting

Meeting number: 130 073 1007

Password: MguMnPN@422

https://intel.webex.com/intel/j.php?MTID=m6bf2875d89c1ee88ca37eda8fd41a47b

Join by video system

Dial 1300731007@...

You can also dial 173.243.2.68 and enter your meeting number.

Join by phone

+1-210-795-1110 US Toll

+1-866-662-9987 US Toll Free

Access code: 130 073 1007

(Webex can be downloaded from https://www.webex.com/downloads.html/.)


Re: [EFI Redfish Host Interface PATCH 5/5] RedfishPkg: Add RedfishHostInterfaceDxe to RedfishPkg

Abner Chang
 

-----Original Message-----
From: Wang, Nickle (HPS SW)
Sent: Thursday, October 29, 2020 2:37 PM
To: Chang, Abner (HPS SW/FW Technologist) <abner.chang@...>;
devel@edk2.groups.io
Cc: Jiaxin Wu <jiaxin.wu@...>; Siyuan Fu <siyuan.fu@...>; Fan
Wang <fan.wang@...>; Jiewen Yao <jiewen.yao@...>
Subject: RE: [EFI Redfish Host Interface PATCH 5/5] RedfishPkg: Add
RedfishHostInterfaceDxe to RedfishPkg

Hi Abner,

Do we need to specify the library instance under [Components] section
when they are specified in [LibraryClasses] already?
Yes, we have to list the library provided by this package in [Components] section, CI also checks this.

Thanks,
Nickle

-----Original Message-----
From: Chang, Abner (HPS SW/FW Technologist) <abner.chang@...>
Sent: Tuesday, October 13, 2020 12:08 PM
To: devel@edk2.groups.io
Cc: Jiaxin Wu <jiaxin.wu@...>; Siyuan Fu <siyuan.fu@...>;
Fan Wang <fan.wang@...>; Jiewen Yao <jiewen.yao@...>;
Wang, Nickle (HPS SW) <nickle.wang@...>
Subject: [EFI Redfish Host Interface PATCH 5/5] RedfishPkg: Add
RedfishHostInterfaceDxe to RedfishPkg

Add RedfishHostInterfaceDxe and PlatformHostInterfaceLibNull to
Redfish package.

Signed-off-by: Abner Chang <abner.chang@...>

Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Fan Wang <fan.wang@...>
Cc: Jiewen Yao <jiewen.yao@...>
Cc: Nickle Wang <nickle.wang@...>
---
RedfishPkg/RedfishPkg.dsc | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/RedfishPkg/RedfishPkg.dsc b/RedfishPkg/RedfishPkg.dsc
index 8acadddefc..be6996b203 100644
--- a/RedfishPkg/RedfishPkg.dsc
+++ b/RedfishPkg/RedfishPkg.dsc
@@ -38,3 +38,9 @@

DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTabl
eLib.inf
DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf

ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/Dx
eReportStatusCodeLib.inf
+
RedfishPlatformHostInterfaceLib|RedfishPkg/Library/PlatformHostInterfa
RedfishPlatformHostInterfaceLib|ce
LibNull/PlatformHostInterfaceLibNull.inf
+
+[Components]
+
RedfishPkg/Library/PlatformHostInterfaceLibNull/PlatformHostInterfaceL
ibN
ull.inf
+
+ RedfishPkg/RedfishHostInterfaceDxe/RedfishHostInterfaceDxe.inf
--
2.17.1


Re: [DxeHttpIoLib PATCH V3 1/3] NetworkPkg/Library: Implementation of Http IO Helper Library

Abner Chang
 

-----Original Message-----
From: Rabeda, Maciej [mailto:maciej.rabeda@...]
Sent: Thursday, October 29, 2020 2:58 AM
To: Chang, Abner (HPS SW/FW Technologist) <abner.chang@...>;
devel@edk2.groups.io
Cc: Jiaxin Wu <jiaxin.wu@...>; Siyuan Fu <siyuan.fu@...>;
Wang, Nickle (HPS SW) <nickle.wang@...>
Subject: Re: [DxeHttpIoLib PATCH V3 1/3] NetworkPkg/Library:
Implementation of Http IO Helper Library

Hi Abner,

Review done - comments inline. Sorry to be picky at times, but we all want
quality code in our UEFI FWs :)
That's no problem Maciej. V4 is sent and some feedbacks to your comments in line.


Thanks,
Maciej

On 26-Oct-20 07:05, Abner Chang wrote:
Add HTTP IO helper library which could be used by HTTP applications
such as HTTP Boot, Redfish HTTP REST EX driver instance and etc.

Signed-off-by: Abner Chang <abner.chang@...>

Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Nickle Wang <nickle.wang@...>
---
NetworkPkg/Include/Library/HttpIoLib.h | 328 +++++++
.../Library/DxeHttpIoLib/DxeHttpIoLib.c | 823 ++++++++++++++++++
.../Library/DxeHttpIoLib/DxeHttpIoLib.inf | 43 +
.../Library/DxeHttpIoLib/DxeHttpIoLib.uni | 13 +
4 files changed, 1207 insertions(+)
create mode 100644 NetworkPkg/Include/Library/HttpIoLib.h
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni

diff --git a/NetworkPkg/Include/Library/HttpIoLib.h
b/NetworkPkg/Include/Library/HttpIoLib.h
new file mode 100644
index 0000000000..8f3804ca42
--- /dev/null
+++ b/NetworkPkg/Include/Library/HttpIoLib.h
@@ -0,0 +1,328 @@
+/** @file
+ HttpIoLib.h.
+
+(C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef HTTP_IO_LIB_H_
+#define HTTP_IO_LIB_H_
+
+#include <IndustryStandard/Http11.h>
+
+#include <Library/DpcLib.h>
+#include <Library/HttpLib.h>
+#include <Library/NetLib.h>
+
+#define HTTP_IO_MAX_SEND_PAYLOAD 1024
+#define HTTP_IO_CHUNK_SIZE_STRING_LEN 50
+#define HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH 256
+
+///
+/// HTTP_IO_CALLBACK_EVENT
+///
+typedef enum {
+ HttpIoRequest,
+ HttpIoResponse
+} HTTP_IO_CALLBACK_EVENT;
+
+/**
+ HttpIo Callback function which will be invoked when specified
HTTP_IO_CALLBACK_EVENT happened.
+
+ @param[in] EventType Indicate the Event type that occurs in the
current callback.
+ @param[in] Message HTTP message which will be send to, or just
received from HTTP server.
+ @param[in] Context The Callback Context pointer.
+
+ @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
+ @retval Others Tells the HttpIo to abort the current HTTP process.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * HTTP_IO_CALLBACK) (
+ IN HTTP_IO_CALLBACK_EVENT EventType,
+ IN EFI_HTTP_MESSAGE *Message,
+ IN VOID *Context
+ );
+
+///
+/// A wrapper structure to hold the received HTTP response data.
+///
+typedef struct {
+ EFI_HTTP_RESPONSE_DATA Response;
+ UINTN HeaderCount;
+ EFI_HTTP_HEADER *Headers;
+ UINTN BodyLength;
+ CHAR8 *Body;
+ EFI_STATUS Status;
+} HTTP_IO_RESPONSE_DATA;
+
+///
+/// HTTP_IO configuration data for IPv4 /// typedef struct {
+ EFI_HTTP_VERSION HttpVersion;
+ UINT32 RequestTimeOut; ///< In milliseconds.
+ UINT32 ResponseTimeOut; ///< In milliseconds.
+ BOOLEAN UseDefaultAddress;
+ EFI_IPv4_ADDRESS LocalIp;
+ EFI_IPv4_ADDRESS SubnetMask;
+ UINT16 LocalPort;
+} HTTP4_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO configuration data for IPv6 /// typedef struct {
+ EFI_HTTP_VERSION HttpVersion;
+ UINT32 RequestTimeOut; ///< In milliseconds.
+ BOOLEAN UseDefaultAddress;
+ EFI_IPv6_ADDRESS LocalIp;
+ UINT16 LocalPort;
+} HTTP6_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO configuration
+///
+typedef union {
+ HTTP4_IO_CONFIG_DATA Config4;
+ HTTP6_IO_CONFIG_DATA Config6;
+} HTTP_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO wrapper of the EFI HTTP service.
+///
+typedef struct {
+ UINT8 IpVersion;
+ EFI_HANDLE Image;
+ EFI_HANDLE Controller;
+ EFI_HANDLE Handle;
+
+ EFI_HTTP_PROTOCOL *Http;
+
+ HTTP_IO_CALLBACK Callback;
+ VOID *Context;
+
+ EFI_HTTP_TOKEN ReqToken;
+ EFI_HTTP_MESSAGE ReqMessage;
+ EFI_HTTP_TOKEN RspToken;
+ EFI_HTTP_MESSAGE RspMessage;
+
+ BOOLEAN IsTxDone;
+ BOOLEAN IsRxDone;
+
+ EFI_EVENT TimeoutEvent;
+ UINT32 Timeout;
+} HTTP_IO;
+
+///
+/// Process code of HTTP chunk transfer.
+///
+typedef enum {
+ HttpIoSendChunkNone = 0,
+ HttpIoSendChunkHeaderZeroContent,
+ HttpIoSendChunkContent,
+ HttpIoSendChunkEndChunk,
+ HttpIoSendChunkFinish
+} HTTP_IO_SEND_CHUNK_PROCESS;
+
+///
+/// Process code of HTTP non chunk transfer.
+///
+typedef enum {
+ HttpIoSendNonChunkNone = 0,
+ HttpIoSendNonChunkHeaderZeroContent,
+ HttpIoSendNonChunkContent,
+ HttpIoSendNonChunkFinish
+} HTTP_IO_SEND_NON_CHUNK_PROCESS;
+
+///
+/// Chunk links for HTTP chunked transfer coding.
+///
+typedef struct {
+ LIST_ENTRY NextChunk;
+ UINTN Length;
+ CHAR8 *Data;
+} HTTP_IO_CHUNKS;
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotifyDpc (
+ IN VOID *Context
+ );
+
+/**
+ Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Destroy the HTTP_IO and release the resources.
+
+ @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be
destroyed.
+
+**/
+VOID
+HttpIoDestroyIo (
+ IN HTTP_IO *HttpIo
+ );
+
+/**
+ Create a HTTP_IO to access the HTTP service. It will create and
+configure
+ a HTTP child handle.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
+ @param[in] ConfigData The HTTP_IO configuration data.
+ @param[in] Callback Callback function which will be invoked when
specified
+ HTTP_IO_CALLBACK_EVENT happened.
+ @param[in] Context The Context data which will be passed to the
Callback function.
+ @param[out] HttpIo The HTTP_IO.
+
+ @retval EFI_SUCCESS The HTTP_IO is created and configured.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_UNSUPPORTED One or more of the control options are
not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Failed to create the HTTP_IO or configure it.
+
+**/
+EFI_STATUS
+HttpIoCreateIo (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN UINT8 IpVersion,
+ IN HTTP_IO_CONFIG_DATA *ConfigData,
+ IN HTTP_IO_CALLBACK Callback,
+ IN VOID *Context,
+ OUT HTTP_IO *HttpIo
+ );
+
+/**
+ Synchronously send a HTTP REQUEST message to the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] Request A pointer to storage such data as URL and
HTTP method.
+ @param[in] HeaderCount Number of HTTP header structures in
Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[in] BodyLength Length in bytes of the HTTP body.
+ @param[in] Body Body associated with the HTTP request.
+
+ @retval EFI_SUCCESS The HTTP request is transmitted.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error
occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoSendRequest (
+ IN HTTP_IO *HttpIo,
+ IN EFI_HTTP_REQUEST_DATA *Request, OPTIONAL
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers, OPTIONAL
+ IN UINTN BodyLength,
+ IN VOID *Body OPTIONAL
+ );
+
+/**
+ Synchronously receive a HTTP RESPONSE message from the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] RecvMsgHeader TRUE to receive a new HTTP response
(from message header).
+ FALSE to continue receive the previous response message.
+ @param[out] ResponseData Point to a wrapper of the received
response data.
+
+ @retval EFI_SUCCESS The HTTP response is received.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error
occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoRecvResponse (
+ IN HTTP_IO *HttpIo,
+ IN BOOLEAN RecvMsgHeader,
+ OUT HTTP_IO_RESPONSE_DATA *ResponseData
+ );
+
+/**
+ Get the value of the content length if there is a "Content-Length" header.
+
+ @param[in] HeaderCount Number of HTTP header structures in
Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ContentLength Pointer to save the value of the content
length.
+
+ @retval EFI_SUCCESS Successfully get the content length.
+ @retval EFI_NOT_FOUND No "Content-Length" header in the
Headers.
+
+**/
+EFI_STATUS
+HttpIoGetContentLength (
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT UINTN *ContentLength
+ );
+
+/**
+ Synchronously receive a HTTP RESPONSE message from the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] HeaderCount Number of headers in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ChunkListHead A pointer to receivce list head of chunked
data.
+ Caller has to release memory of ChunkListHead
+ and all list entries.
+ @param[out] ContentLength Total content length
+
+ @retval EFI_SUCCESS The HTTP chunked transfer is received.
+ @retval EFI_NOT_FOUND No chunked transfer coding header found.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_INVALID_PARAMETER Improper parameters.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoGetChunkedTransferContent (
+ IN HTTP_IO *HttpIo,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT LIST_ENTRY **ChunkListHead,
+ OUT UINTN *ContentLength
+ );
+
+/**
+ Send HTTP request in chunks.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] SendChunkProcess Pointer to current chunk process
status.
+ @param[out] RequestMessage Request to send.
+
+ @retval EFI_SUCCESS Successfully to send chunk data according to
SendChunkProcess.
+ @retval Other Other errors.
+
+**/
+EFI_STATUS
+HttpIoSendChunkedTransfer (
+ IN HTTP_IO *HttpIo,
+ IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,
+ IN EFI_HTTP_MESSAGE *RequestMessage
+);
+#endif
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
new file mode 100644
index 0000000000..88d647e42d
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
@@ -0,0 +1,823 @@
+/** @file
+ Http IO Helper Library.
+
+ (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent **/
+
+#include <Uefi.h>
+
+#include <Protocol/Http.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HttpIoLib.h>
+#include <Library/MemoryAllocationLib.h> #include
+<Library/PrintLib.h> #include <Library/UefiBootServicesTableLib.h>
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotifyDpc (
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, Context); }
+
+/**
+ Destroy the HTTP_IO and release the resources.
+
+ @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be
destroyed.
+
+**/
+VOID
+HttpIoDestroyIo (
+ IN HTTP_IO *HttpIo
+ )
+{
+ EFI_HTTP_PROTOCOL *Http;
+ EFI_EVENT Event;
+
+ if (HttpIo == NULL) {
+ return;
+ }
+
+ Event = HttpIo->ReqToken.Event;
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Event = HttpIo->RspToken.Event;
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Event = HttpIo->TimeoutEvent;
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Http = HttpIo->Http;
+ if (Http != NULL) {
+ Http->Configure (Http, NULL);
+ gBS->CloseProtocol (
+ HttpIo->Handle,
+ &gEfiHttpProtocolGuid,
+ HttpIo->Image,
+ HttpIo->Controller
+ );
+ }
+
+ NetLibDestroyServiceChild (
+ HttpIo->Controller,
+ HttpIo->Image,
+ &gEfiHttpServiceBindingProtocolGuid,
+ HttpIo->Handle
+ );
+}
+
+/**
+ Create a HTTP_IO to access the HTTP service. It will create and
+configure
+ a HTTP child handle.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
+ @param[in] ConfigData The HTTP_IO configuration data ,
+ NULL means not to configure the HTTP child.
+ @param[in] Callback Callback function which will be invoked when
specified
+ HTTP_IO_CALLBACK_EVENT happened.
+ @param[in] Context The Context data which will be passed to the
Callback function.
+ @param[out] HttpIo The HTTP_IO.
+
+ @retval EFI_SUCCESS The HTTP_IO is created and configured.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_UNSUPPORTED One or more of the control options are
not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Failed to create the HTTP_IO or configure it.
+
+**/
+EFI_STATUS
+HttpIoCreateIo (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN UINT8 IpVersion,
+ IN HTTP_IO_CONFIG_DATA *ConfigData, OPTIONAL
+ IN HTTP_IO_CALLBACK Callback,
+ IN VOID *Context,
+ OUT HTTP_IO *HttpIo
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_CONFIG_DATA HttpConfigData;
+ EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;
+ EFI_HTTPv6_ACCESS_POINT Http6AccessPoint;
+ EFI_HTTP_PROTOCOL *Http;
+ EFI_EVENT Event;
+
+ if ((Image == NULL) || (Controller == NULL) || (HttpIo == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
+ return EFI_UNSUPPORTED;
+ }
+
+ ZeroMem (HttpIo, sizeof (HTTP_IO)); ZeroMem (&HttpConfigData,
+ sizeof (EFI_HTTP_CONFIG_DATA));
+
+ //
+ // Create the HTTP child instance and get the HTTP protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ Image,
+ &gEfiHttpServiceBindingProtocolGuid,
+ &HttpIo->Handle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ HttpIo->Handle,
+ &gEfiHttpProtocolGuid,
+ (VOID **) &Http,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status) || (Http == NULL)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Init the configuration data and configure the HTTP child.
+ //
+ HttpIo->Image = Image;
+ HttpIo->Controller = Controller;
+ HttpIo->IpVersion = IpVersion;
+ HttpIo->Http = Http;
+ HttpIo->Callback = Callback;
+ HttpIo->Context = Context;
+
+ if (ConfigData != NULL) {
+ if (HttpIo->IpVersion == IP_VERSION_4) {
+ HttpConfigData.LocalAddressIsIPv6 = FALSE;
+ HttpConfigData.HttpVersion = ConfigData->Config4.HttpVersion;
+ HttpConfigData.TimeOutMillisec = ConfigData-
Config4.RequestTimeOut;
+
+ Http4AccessPoint.UseDefaultAddress = ConfigData-
Config4.UseDefaultAddress;
+ Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;
+ IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData-
Config4.LocalIp);
+ IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData-
Config4.SubnetMask);
+ HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
+ } else {
+ HttpConfigData.LocalAddressIsIPv6 = TRUE;
+ HttpConfigData.HttpVersion = ConfigData->Config6.HttpVersion;
+ HttpConfigData.TimeOutMillisec = ConfigData-
Config6.RequestTimeOut;
+
+ Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;
+ IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData-
Config6.LocalIp);
+ HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
+ }
+
+ Status = Http->Configure (Http, &HttpConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Create events for variuos asynchronous operations.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpIoNotify,
+ &HttpIo->IsTxDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ HttpIo->ReqToken.Event = Event;
+ HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpIoNotify,
+ &HttpIo->IsRxDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ HttpIo->RspToken.Event = Event;
+ HttpIo->RspToken.Message = &HttpIo->RspMessage;
+
+ //
+ // Create TimeoutEvent for response // Status = gBS->CreateEvent
+ (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ HttpIo->TimeoutEvent = Event;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ HttpIoDestroyIo (HttpIo);
+
+ return Status;
+}
+
+/**
+ Synchronously send a HTTP REQUEST message to the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] Request A pointer to storage such data as URL and
HTTP method.
+ @param[in] HeaderCount Number of HTTP header structures in
Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[in] BodyLength Length in bytes of the HTTP body.
+ @param[in] Body Body associated with the HTTP request.
+
+ @retval EFI_SUCCESS The HTTP request is trasmitted.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error
occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoSendRequest (
+ IN HTTP_IO *HttpIo,
+ IN EFI_HTTP_REQUEST_DATA *Request,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ IN UINTN BodyLength,
+ IN VOID *Body
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_PROTOCOL *Http;
+
+ if (HttpIo == NULL || HttpIo->Http == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HttpIo->ReqToken.Status = EFI_NOT_READY;
+ HttpIo->ReqToken.Message->Data.Request = Request;
+ HttpIo->ReqToken.Message->HeaderCount = HeaderCount;
+ HttpIo->ReqToken.Message->Headers = Headers;
+ HttpIo->ReqToken.Message->BodyLength = BodyLength;
+ HttpIo->ReqToken.Message->Body = Body;
+
+ if (HttpIo->Callback != NULL) {
+ Status = HttpIo->Callback (
+ HttpIoRequest,
+ HttpIo->ReqToken.Message,
+ HttpIo->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Queue the request token to HTTP instances.
+ //
+ Http = HttpIo->Http;
+ HttpIo->IsTxDone = FALSE;
+ Status = Http->Request (
+ Http,
+ &HttpIo->ReqToken
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Poll the network until transmit finish.
+ //
+ while (!HttpIo->IsTxDone) {
+ Http->Poll (Http);
+ }
+
+ return HttpIo->ReqToken.Status;
+}
+
+/**
+ Synchronously receive a HTTP RESPONSE message from the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] RecvMsgHeader TRUE to receive a new HTTP response
(from message header).
+ FALSE to continue receive the previous response message.
+ @param[out] ResponseData Point to a wrapper of the received
response data.
+
+ @retval EFI_SUCCESS The HTTP response is received.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error
occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoRecvResponse (
+ IN HTTP_IO *HttpIo,
+ IN BOOLEAN RecvMsgHeader,
+ OUT HTTP_IO_RESPONSE_DATA *ResponseData
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_PROTOCOL *Http;
+
+ if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Queue the response token to HTTP instances.
+ //
+ HttpIo->RspToken.Status = EFI_NOT_READY; if (RecvMsgHeader) {
+ HttpIo->RspToken.Message->Data.Response =
+ &ResponseData->Response; } else {
+ HttpIo->RspToken.Message->Data.Response = NULL; }
+ HttpIo->RspToken.Message->HeaderCount = 0;
+ HttpIo->RspToken.Message->Headers = NULL;
+ HttpIo->RspToken.Message->BodyLength = ResponseData-
BodyLength;
+ HttpIo->RspToken.Message->Body = ResponseData->Body;
ResponseData in function header is set as OUT. Since we are actually using
data inside it, it should be IN OUT.
Please fix that in code and parameter comments.
+
+ Http = HttpIo->Http;
+ HttpIo->IsRxDone = FALSE;
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the header
packet.
+ //
+ Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative,
+ HttpIo->Timeout * TICKS_PER_MS); if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Http->Response (
+ Http,
+ &HttpIo->RspToken
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Remove timeout timer from the event list.
+ //
+ gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
+ return Status;
+ }
+
+ //
+ // Poll the network until receive finish.
+ //
+ while (!HttpIo->IsRxDone && EFI_ERROR (gBS->CheckEvent (HttpIo-
TimeoutEvent))) {
+ Http->Poll (Http);
+ }
+
+ //
+ // Remove timeout timer from the event list.
+ //
+ gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
+
+ if (!HttpIo->IsRxDone) {
+ //
+ // Timeout occurs, cancel the response token.
+ //
+ Http->Cancel (Http, &HttpIo->RspToken);
+
+ Status = EFI_TIMEOUT;
+
+ return Status;
+ } else {
+ HttpIo->IsRxDone = FALSE;
+ }
+
+ if ((HttpIo->Callback != NULL) &&
+ (HttpIo->RspToken.Status == EFI_SUCCESS || HttpIo-
RspToken.Status == EFI_HTTP_ERROR)) {
+ Status = HttpIo->Callback (
+ HttpIoResponse,
+ HttpIo->RspToken.Message,
+ HttpIo->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Store the received data into the wrapper.
+ //
+ ResponseData->Status = HttpIo->RspToken.Status;
+ ResponseData->HeaderCount = HttpIo->RspToken.Message-
HeaderCount;
+ ResponseData->Headers = HttpIo->RspToken.Message->Headers;
+ ResponseData->BodyLength = HttpIo->RspToken.Message-
BodyLength;
+
+ return Status;
+}
+
+/**
+ Get the value of the content length if there is a "Content-Length" header.
+
+ @param[in] HeaderCount Number of HTTP header structures in
Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ContentLength Pointer to save the value of the content
length.
+
+ @retval EFI_SUCCESS Successfully get the content length.
+ @retval EFI_NOT_FOUND No "Content-Length" header in the
Headers.
+
+**/
+EFI_STATUS
+HttpIoGetContentLength (
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT UINTN *ContentLength
+ )
+{
+ EFI_HTTP_HEADER *Header;
+
+ Header = HttpFindHeader (HeaderCount, Headers,
+ HTTP_HEADER_CONTENT_LENGTH); if (Header == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **)
+NULL, ContentLength); }
+/**
+ Send HTTP request in chunks.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] SendChunkProcess Pointer to current chunk process
status.
+ @param[in] RequestMessage Request to send.
+
+ @retval EFI_SUCCESS Successfully to send chunk data according to
SendChunkProcess.
+ @retval Other Other errors.
+
+**/
+EFI_STATUS
+HttpIoSendChunkedTransfer (
+ IN HTTP_IO *HttpIo,
+ IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,
+ IN EFI_HTTP_MESSAGE *RequestMessage
+)
+{
+ EFI_STATUS Status;
+ EFI_HTTP_HEADER *NewHeaders;
+ EFI_HTTP_HEADER *ContentLengthHeader;
+ UINTN AddNewHeader;
+ UINTN HeaderCount;
+ CHAR8 *MessageBody;
+ UINTN MessageBodyLength;
+ CHAR8 ChunkLengthStr [HTTP_IO_CHUNK_SIZE_STRING_LEN];
+ EFI_HTTP_REQUEST_DATA *SentRequestData;
+
+ AddNewHeader = 0;
+ NewHeaders = NULL;
+ MessageBody = NULL;
+ ContentLengthHeader = NULL;
+ MessageBodyLength = 0;
+
+ switch (*SendChunkProcess) {
+ case HttpIoSendChunkHeaderZeroContent:
+ ContentLengthHeader = HttpFindHeader (RequestMessage-
HeaderCount, RequestMessage->Headers,
HTTP_HEADER_CONTENT_LENGTH);
+ if (ContentLengthHeader == NULL) {
+ AddNewHeader = 1;
+ }
+
+ NewHeaders = AllocateZeroPool ((RequestMessage->HeaderCount +
AddNewHeader) * sizeof(EFI_HTTP_HEADER));
+ CopyMem ((VOID*)NewHeaders, (VOID *)RequestMessage->Headers,
RequestMessage->HeaderCount * sizeof (EFI_HTTP_HEADER));
+ if (AddNewHeader == 0) {
+ //
+ // Override content-length to Transfer-Encoding.
+ //
+ ContentLengthHeader = HttpFindHeader (RequestMessage-
HeaderCount, NewHeaders, HTTP_HEADER_CONTENT_LENGTH);
+ ContentLengthHeader->FieldName = NULL;
+ ContentLengthHeader->FieldValue = NULL;
+ } else {
+ ContentLengthHeader = NewHeaders + RequestMessage-
HeaderCount;
+ }
+ HttpSetFieldNameAndValue (ContentLengthHeader,
HTTP_HEADER_TRANSFER_ENCODING,
HTTP_HEADER_TRANSFER_ENCODING_CHUNKED);
+ HeaderCount = RequestMessage->HeaderCount + AddNewHeader;
+ MessageBodyLength = 0;
+ MessageBody = NULL;
+ SentRequestData = RequestMessage->Data.Request;
+ break;
+
+ case HttpIoSendChunkContent:
+ HeaderCount = 0;
+ NewHeaders = NULL;
+ SentRequestData = NULL;
+ if (RequestMessage->BodyLength > HTTP_IO_MAX_SEND_PAYLOAD) {
+ MessageBodyLength = HTTP_IO_MAX_SEND_PAYLOAD;
+ } else {
+ MessageBodyLength = RequestMessage->BodyLength;
+ }
+ AsciiSPrint (
+ ChunkLengthStr,
+ HTTP_IO_CHUNK_SIZE_STRING_LEN,
+ "%x%c%c",
+ MessageBodyLength,
+ CHUNKED_TRANSFER_CODING_CR,
+ CHUNKED_TRANSFER_CODING_LF
+ );
Function breaks should be off by 2 spaces, not 4 :) This applies to other
broken-down calls in chunked send/receive functions.
Done.

+ MessageBody = AllocatePool (AsciiStrLen (ChunkLengthStr) +
MessageBodyLength + 2);
+ if (MessageBody == NULL) {
+ DEBUG((DEBUG_ERROR, "Not enough memory for chunk
transfer\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Build up the chunk transfer paylaod.
+ //
+ CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen
+ (ChunkLengthStr));
AsciiStrLen is used a lot here. It could be called once and stored in variable.
Done

+ CopyMem (MessageBody + AsciiStrLen (ChunkLengthStr),
RequestMessage->Body, MessageBodyLength);
+ *(MessageBody + AsciiStrLen (ChunkLengthStr) + MessageBodyLength)
= CHUNKED_TRANSFER_CODING_CR;
+ *(MessageBody + AsciiStrLen (ChunkLengthStr) + MessageBodyLength
+ 1) = CHUNKED_TRANSFER_CODING_LF;
+ //
+ // Change variables for the next chunk trasnfer.
+ //
+ RequestMessage->BodyLength -= MessageBodyLength;
+ RequestMessage->Body = (VOID *)((CHAR8 *)RequestMessage-
Body + MessageBodyLength);
+ MessageBodyLength += (AsciiStrLen (ChunkLengthStr) + 2);
+ if (RequestMessage->BodyLength == 0) {
+ *SendChunkProcess = HttpIoSendChunkEndChunk;
+ }
+ break;
+
+ case HttpIoSendChunkEndChunk:
+ HeaderCount = 0;
+ NewHeaders = NULL;
+ SentRequestData = NULL;
+ AsciiSPrint (
+ ChunkLengthStr,
+ HTTP_IO_CHUNK_SIZE_STRING_LEN,
+ "0%c%c%c%c",
+ CHUNKED_TRANSFER_CODING_CR,
+ CHUNKED_TRANSFER_CODING_LF,
+ CHUNKED_TRANSFER_CODING_CR,
+ CHUNKED_TRANSFER_CODING_LF
+ );
+ MessageBody = AllocatePool (AsciiStrLen(ChunkLengthStr));
+ if (MessageBody == NULL) {
+ DEBUG((DEBUG_ERROR, "Not enough memory for the end chunk
transfer\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen
(ChunkLengthStr));
+ MessageBodyLength = AsciiStrLen (ChunkLengthStr);
+ *SendChunkProcess = HttpIoSendChunkFinish;
+ break;
+
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = HttpIoSendRequest (
+ HttpIo,
+ SentRequestData,
+ HeaderCount,
+ NewHeaders,
+ MessageBodyLength,
+ MessageBody
+ );
+ if (ContentLengthHeader != NULL) {
+ if (ContentLengthHeader->FieldName != NULL) {
+ FreePool (ContentLengthHeader->FieldName);
+ }
+ if (ContentLengthHeader->FieldValue != NULL) {
+ FreePool (ContentLengthHeader->FieldValue);
+ }
+ }
+ if (NewHeaders != NULL) {
+ FreePool (NewHeaders);
+ }
+ if (MessageBody != NULL) {
+ FreePool (MessageBody);
+ }
+ return Status;
+}
+
+/**
+ Synchronously receive a HTTP RESPONSE message from the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] HeaderCount Number of headers in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ChunkListHead A pointer to receive list head
+ of chunked data. Caller has to
+ release memory of ChunkListHead
+ and all list entries.
+ @param[out] ContentLength Total content length
+
+ @retval EFI_SUCCESS The HTTP chunked transfer is received.
+ @retval EFI_NOT_FOUND No chunked transfer coding header found.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_INVALID_PARAMETER Improper parameters.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoGetChunkedTransferContent (
+ IN HTTP_IO *HttpIo,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT LIST_ENTRY **ChunkListHead,
+ OUT UINTN *ContentLength
+ )
+{
+ EFI_HTTP_HEADER *Header;
+ CHAR8 ChunkSizeAscii [256];
+ EFI_STATUS Status;
+ UINTN Index;
+ HTTP_IO_RESPONSE_DATA ResponseData;
+ UINTN TotalLength;
+ LIST_ENTRY *HttpChunks;
+ HTTP_IO_CHUNKS *ThisChunk;
+ LIST_ENTRY *ThisListEntry;
+
+ if (ChunkListHead == NULL || ContentLength == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *ContentLength = 0;
+ Header = HttpFindHeader (HeaderCount, Headers,
+ HTTP_HEADER_TRANSFER_ENCODING); if (Header == NULL) {
+ return EFI_NOT_FOUND;
+ }
+ if (AsciiStrCmp (Header->FieldValue,
HTTP_HEADER_TRANSFER_ENCODING_CHUNKED) != 0) {
+ return EFI_NOT_FOUND;
+ }
Please add a bit of vertical spacing between "blocks" of code that perform a
logical subroutine inside this function (parameter prep, function call, result,
spacing).
It really is easier to read!
Fixed.
+ //
+ // Loop to get all chunks.
+ //
+ TotalLength = 0;
+ HttpChunks = (LIST_ENTRY *)AllocateZeroPool (sizeof (LIST_ENTRY));
+ if (HttpChunks == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ InitializeListHead (HttpChunks);
+ DEBUG ((DEBUG_INFO, " Chunked transfer\n"));
+ while (TRUE) {
+ ZeroMem((VOID *)&ResponseData,
sizeof(HTTP_IO_RESPONSE_DATA));
+ ResponseData.BodyLength =
HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH;
+ ResponseData.Body = ChunkSizeAscii;
+ Status = HttpIoRecvResponse (
+ HttpIo,
+ FALSE,
+ &ResponseData
+ );
+ if (EFI_ERROR (Status)) {
+ goto ExitDeleteChunks;
+ }
+ //
+ // Decoding Chunked Transfer Coding.
+ // Only decode chunk-size and last chunk.
+ //
+ DEBUG ((DEBUG_INFO, " Chunk HTTP Response StatusCode - %d\n",
ResponseData.Response.StatusCode));
+ //
+ // Break if this is last chunk.
+ //
+ if (ChunkSizeAscii [0] == CHUNKED_TRANSFER_CODING_LAST_CHUNK)
{
+ Status = EFI_SUCCESS;
+ DEBUG ((DEBUG_INFO, " Last chunk\n"));
+ ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof
(HTTP_IO_CHUNKS));
+ if (ThisChunk == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ InitializeListHead (&ThisChunk->NextChunk);
+ ThisChunk->Length = ResponseData.BodyLength - 1 - 2; // Minus sizeof
'0' and CRLF.
How do we know that the chunk we have received even contains 0 and CRLF?
I would suggest adding minimal chunk length validation or else
ThisChunk->Length might underflow.
Sure. additional checking is added. Thanks for this feedback.


+ ThisChunk->Data = (CHAR8 *)AllocatePool (ThisChunk->Length);
+ if (ThisChunk->Data == NULL) {
+ FreePool ((UINT8 *)ThisChunk);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ CopyMem ((UINT8 *)ThisChunk->Data, (UINT8 *)ResponseData.Body
+ 1, ThisChunk->Length);
+ TotalLength += ThisChunk->Length;
+ InsertTailList (HttpChunks, &ThisChunk->NextChunk);
+ break;
+ }
+
+ //
+ // Get the chunk length
+ //
+ Index = 0;
+ while ((ChunkSizeAscii [Index] !=
CHUNKED_TRANSFER_CODING_EXTENSION_SEPARATOR) &&
+ (ChunkSizeAscii [Index] !=
(CHAR8)CHUNKED_TRANSFER_CODING_CR) &&
+ (Index != HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH))
{
+ Index ++;
+ };
Comma after while block - please remove.
+ if (Index == HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH) {
+ Status = EFI_NOT_FOUND;
+ goto ExitDeleteChunks;
+ }
+ ChunkSizeAscii[Index] = 0;
+ AsciiStrHexToUintnS (ChunkSizeAscii, NULL, ContentLength);
+ DEBUG ((DEBUG_INFO, " Length of this chunk %d\n",
*ContentLength));
I do not trust chunk input. If *ContentLength is something huge, we might
overflow TotalLength.
Knowing the message length (ResponseData.BodyLength), we could obtain
how much space the actual chunk can occupy.
It would be good to verify *ContentLength against the size of that space, so
that we do not allocate some huge block later.
ResponseData.BodyLength is given by *ContentLength and HttpIoRecvResponse only copies data read from TCP in ResponseData.BodyLength to ResponseData.Body.
Furthermore,
If *ContentLength is huge but the response payload is returned in the same length, then we can just trust it with the checking the end "\r\n" of chunk payload.
If *ContentLength is larger than the response payload can be returned by HTTP service then HttpIoRecvResponse suppose returns timeout or some other EFI errors. Then we exit chunk function with error code.
If *ContentLength is smaller than the response payload returned by HTTP service, the checking of end "\r\n" of chunk payload will be failed. But this case shouldn't happen in HttpIoRecvResponse function.
Currently we read the end "\r\n" of chunk payload however we don’t verify it. To add additional check on end "\r\n" of chunk at least can make sure the chunk is correct.
Make sense?
You can check v4 patch set for the corresponding changes.

+ //
+ // Receive the data;
+ //
+ ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof
(HTTP_IO_CHUNKS));
+ if (ThisChunk == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ ResponseData.BodyLength = *ContentLength;
+ ResponseData.Body = (CHAR8 *)AllocatePool (*ContentLength);
+ if (ResponseData.Body == NULL) {
+ FreePool (ThisChunk);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ InitializeListHead (&ThisChunk->NextChunk);
+ ThisChunk->Length = *ContentLength;
+ ThisChunk->Data = ResponseData.Body;
+ InsertTailList (HttpChunks, &ThisChunk->NextChunk);
+ Status = HttpIoRecvResponse (
+ HttpIo,
+ FALSE,
+ &ResponseData
+ );
+ if (EFI_ERROR (Status)) {
+ goto ExitDeleteChunks;
+ }
+ //
+ // Read CRLF
+ //
+ ZeroMem((VOID *)&ResponseData,
sizeof(HTTP_IO_RESPONSE_DATA));
Spacing after function name.
+ ResponseData.BodyLength = 2;
+ ResponseData.Body = ChunkSizeAscii;
+ Status = HttpIoRecvResponse (
+ HttpIo,
+ FALSE,
+ &ResponseData
+ );
+ if (EFI_ERROR (Status)) {
+ goto ExitDeleteChunks;
+ }
+
+ TotalLength += *ContentLength;
+ };
Comma after while block. Please remove.
Done


+
+ *ContentLength = TotalLength;
+ *ChunkListHead = HttpChunks;
+ DEBUG ((DEBUG_INFO, " Total of lengh of chunks :%d\n",
TotalLength));
+ return EFI_SUCCESS;
+
+ExitDeleteChunks:
+ if (HttpChunks != NULL) {
+ while (!IsListEmpty(HttpChunks)) {
+ ThisListEntry = GetFirstNode (HttpChunks);
+ RemoveEntryList (ThisListEntry);
+ ThisChunk = (HTTP_IO_CHUNKS *)ThisListEntry;
+ if (ThisChunk->Data != NULL) {
+ FreePool (ThisChunk->Data);
+ }
+ FreePool(ThisListEntry);
+ };
Comma after while block. Please remove.
Done

+ FreePool (HttpChunks);
+ }
+ return Status;
+}
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
new file mode 100644
index 0000000000..a02b409547
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
@@ -0,0 +1,43 @@
+## @file
+# This library instance provides HTTP IO helper functions.
+#
+# (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR> #
+SPDX-License-Identifier: BSD-2-Clause-Patent # ##
+
+[Defines]
+ INF_VERSION = 0x0001001b
+ BASE_NAME = DxeHttpIoLib
+ MODULE_UNI_FILE = DxeHttpIoLib.uni
+ FILE_GUID = 50B198F8-7986-4F51-A857-CFE4643D59F3
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = HttpIoLib| DXE_CORE DXE_DRIVER
DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION
UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the
build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC ARM AARM64 RISCV64
+#
+
+[Sources]
+ DxeHttpIoLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ NetworkPkg/NetworkPkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DpcLib
+ MemoryAllocationLib
+ PrintLib
+ UefiBootServicesTableLib
+
+[Protocols]
+ gEfiHttpProtocolGuid ## SOMETIMES_CONSUMES
+
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni
b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni
new file mode 100644
index 0000000000..d419b1a49d
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni
@@ -0,0 +1,13 @@
+// /** @file
+// The library instance provides HTTP IO helper functions.
+//
+// (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent // // **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "HTTP IO Helper
Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "The library
instance provides HTTP IO helper functions."
+


[DxeHttpIoLib PATCH V4 3/3] NetworkPkg/HttpBootDxe: Utilize HttpIoLib

Abner Chang
 

Remove HTTP IO realted funcitons from HttpBootDxe
and use HttpIoLib instead.

Signed-off-by: Abner Chang <abner.chang@...>

Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Nickle Wang <nickle.wang@...>
---
NetworkPkg/HttpBootDxe/HttpBootDxe.h | 3 +-
NetworkPkg/HttpBootDxe/HttpBootDxe.inf | 2 +
NetworkPkg/HttpBootDxe/HttpBootSupport.c | 431 +----------------------
NetworkPkg/HttpBootDxe/HttpBootSupport.h | 189 +---------
4 files changed, 6 insertions(+), 619 deletions(-)

diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.h b/NetworkPkg/HttpBootDxe/HttpBootDxe.h
index 0b45f9de0b..d692c3ad3c 100644
--- a/NetworkPkg/HttpBootDxe/HttpBootDxe.h
+++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.h
@@ -2,7 +2,7 @@
UEFI HTTP boot driver's private data structure and interfaces declaration.

Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
-(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+(C) Copyright 2016 - 2020 Hewlett Packard Enterprise Development LP<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/
@@ -28,6 +28,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#include <Library/DebugLib.h>
#include <Library/NetLib.h>
#include <Library/HttpLib.h>
+#include <Library/HttpIoLib.h>
#include <Library/HiiLib.h>
#include <Library/PrintLib.h>
#include <Library/DpcLib.h>
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.inf b/NetworkPkg/HttpBootDxe/HttpBootDxe.inf
index 5beab728dd..a27a561722 100644
--- a/NetworkPkg/HttpBootDxe/HttpBootDxe.inf
+++ b/NetworkPkg/HttpBootDxe/HttpBootDxe.inf
@@ -2,6 +2,7 @@
# This modules produce the Load File Protocol for UEFI HTTP boot.
#
# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
@@ -52,6 +53,7 @@
DebugLib
NetLib
HttpLib
+ HttpIoLib
HiiLib
PrintLib
DpcLib
diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.c b/NetworkPkg/HttpBootDxe/HttpBootSupport.c
index 5b0e054a05..93d9dfc464 100644
--- a/NetworkPkg/HttpBootDxe/HttpBootSupport.c
+++ b/NetworkPkg/HttpBootDxe/HttpBootSupport.c
@@ -2,7 +2,7 @@
Support functions implementation for UEFI HTTP boot driver.

Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
-(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+(C) Copyright 2016 - 2020 Hewlett Packard Enterprise Development LP<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/
@@ -622,435 +622,6 @@ HttpBootSetHeader (
return EFI_SUCCESS;
}

-/**
- Notify the callback function when an event is triggered.
-
- @param[in] Context The opaque parameter to the function.
-
-**/
-VOID
-EFIAPI
-HttpIoNotifyDpc (
- IN VOID *Context
- )
-{
- *((BOOLEAN *) Context) = TRUE;
-}
-
-/**
- Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
-
- @param[in] Event The event signaled.
- @param[in] Context The opaque parameter to the function.
-
-**/
-VOID
-EFIAPI
-HttpIoNotify (
- IN EFI_EVENT Event,
- IN VOID *Context
- )
-{
- //
- // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK
- //
- QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, Context);
-}
-
-/**
- Create a HTTP_IO to access the HTTP service. It will create and configure
- a HTTP child handle.
-
- @param[in] Image The handle of the driver image.
- @param[in] Controller The handle of the controller.
- @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
- @param[in] ConfigData The HTTP_IO configuration data.
- @param[in] Callback Callback function which will be invoked when specified
- HTTP_IO_CALLBACK_EVENT happened.
- @param[in] Context The Context data which will be passed to the Callback function.
- @param[out] HttpIo The HTTP_IO.
-
- @retval EFI_SUCCESS The HTTP_IO is created and configured.
- @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
- @retval EFI_UNSUPPORTED One or more of the control options are not
- supported in the implementation.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- @retval Others Failed to create the HTTP_IO or configure it.
-
-**/
-EFI_STATUS
-HttpIoCreateIo (
- IN EFI_HANDLE Image,
- IN EFI_HANDLE Controller,
- IN UINT8 IpVersion,
- IN HTTP_IO_CONFIG_DATA *ConfigData,
- IN HTTP_IO_CALLBACK Callback,
- IN VOID *Context,
- OUT HTTP_IO *HttpIo
- )
-{
- EFI_STATUS Status;
- EFI_HTTP_CONFIG_DATA HttpConfigData;
- EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;
- EFI_HTTPv6_ACCESS_POINT Http6AccessPoint;
- EFI_HTTP_PROTOCOL *Http;
- EFI_EVENT Event;
-
- if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
- return EFI_UNSUPPORTED;
- }
-
- ZeroMem (HttpIo, sizeof (HTTP_IO));
-
- //
- // Create the HTTP child instance and get the HTTP protocol.
- //
- Status = NetLibCreateServiceChild (
- Controller,
- Image,
- &gEfiHttpServiceBindingProtocolGuid,
- &HttpIo->Handle
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- Status = gBS->OpenProtocol (
- HttpIo->Handle,
- &gEfiHttpProtocolGuid,
- (VOID **) &Http,
- Image,
- Controller,
- EFI_OPEN_PROTOCOL_BY_DRIVER
- );
- if (EFI_ERROR (Status) || (Http == NULL)) {
- goto ON_ERROR;
- }
-
- //
- // Init the configuration data and configure the HTTP child.
- //
- HttpIo->Image = Image;
- HttpIo->Controller = Controller;
- HttpIo->IpVersion = IpVersion;
- HttpIo->Http = Http;
- HttpIo->Callback = Callback;
- HttpIo->Context = Context;
-
- ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
- HttpConfigData.HttpVersion = HttpVersion11;
- HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;
- if (HttpIo->IpVersion == IP_VERSION_4) {
- HttpConfigData.LocalAddressIsIPv6 = FALSE;
-
- Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
- Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;
- IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
- IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
- HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
- } else {
- HttpConfigData.LocalAddressIsIPv6 = TRUE;
- Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;
- IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);
- HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
- }
-
- Status = Http->Configure (Http, &HttpConfigData);
- if (EFI_ERROR (Status)) {
- goto ON_ERROR;
- }
-
- //
- // Create events for various asynchronous operations.
- //
- Status = gBS->CreateEvent (
- EVT_NOTIFY_SIGNAL,
- TPL_NOTIFY,
- HttpIoNotify,
- &HttpIo->IsTxDone,
- &Event
- );
- if (EFI_ERROR (Status)) {
- goto ON_ERROR;
- }
- HttpIo->ReqToken.Event = Event;
- HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
-
- Status = gBS->CreateEvent (
- EVT_NOTIFY_SIGNAL,
- TPL_NOTIFY,
- HttpIoNotify,
- &HttpIo->IsRxDone,
- &Event
- );
- if (EFI_ERROR (Status)) {
- goto ON_ERROR;
- }
- HttpIo->RspToken.Event = Event;
- HttpIo->RspToken.Message = &HttpIo->RspMessage;
-
- //
- // Create TimeoutEvent for response
- //
- Status = gBS->CreateEvent (
- EVT_TIMER,
- TPL_CALLBACK,
- NULL,
- NULL,
- &Event
- );
- if (EFI_ERROR (Status)) {
- goto ON_ERROR;
- }
- HttpIo->TimeoutEvent = Event;
-
- return EFI_SUCCESS;
-
-ON_ERROR:
- HttpIoDestroyIo (HttpIo);
-
- return Status;
-}
-
-/**
- Destroy the HTTP_IO and release the resources.
-
- @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
-
-**/
-VOID
-HttpIoDestroyIo (
- IN HTTP_IO *HttpIo
- )
-{
- EFI_HTTP_PROTOCOL *Http;
- EFI_EVENT Event;
-
- if (HttpIo == NULL) {
- return;
- }
-
- Event = HttpIo->ReqToken.Event;
- if (Event != NULL) {
- gBS->CloseEvent (Event);
- }
-
- Event = HttpIo->RspToken.Event;
- if (Event != NULL) {
- gBS->CloseEvent (Event);
- }
-
- Event = HttpIo->TimeoutEvent;
- if (Event != NULL) {
- gBS->CloseEvent (Event);
- }
-
- Http = HttpIo->Http;
- if (Http != NULL) {
- Http->Configure (Http, NULL);
- gBS->CloseProtocol (
- HttpIo->Handle,
- &gEfiHttpProtocolGuid,
- HttpIo->Image,
- HttpIo->Controller
- );
- }
-
- NetLibDestroyServiceChild (
- HttpIo->Controller,
- HttpIo->Image,
- &gEfiHttpServiceBindingProtocolGuid,
- HttpIo->Handle
- );
-}
-
-/**
- Synchronously send a HTTP REQUEST message to the server.
-
- @param[in] HttpIo The HttpIo wrapping the HTTP service.
- @param[in] Request A pointer to storage such data as URL and HTTP method.
- @param[in] HeaderCount Number of HTTP header structures in Headers list.
- @param[in] Headers Array containing list of HTTP headers.
- @param[in] BodyLength Length in bytes of the HTTP body.
- @param[in] Body Body associated with the HTTP request.
-
- @retval EFI_SUCCESS The HTTP request is transmitted.
- @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
- @retval Others Other errors as indicated.
-
-**/
-EFI_STATUS
-HttpIoSendRequest (
- IN HTTP_IO *HttpIo,
- IN EFI_HTTP_REQUEST_DATA *Request,
- IN UINTN HeaderCount,
- IN EFI_HTTP_HEADER *Headers,
- IN UINTN BodyLength,
- IN VOID *Body
- )
-{
- EFI_STATUS Status;
- EFI_HTTP_PROTOCOL *Http;
-
- if (HttpIo == NULL || HttpIo->Http == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- HttpIo->ReqToken.Status = EFI_NOT_READY;
- HttpIo->ReqToken.Message->Data.Request = Request;
- HttpIo->ReqToken.Message->HeaderCount = HeaderCount;
- HttpIo->ReqToken.Message->Headers = Headers;
- HttpIo->ReqToken.Message->BodyLength = BodyLength;
- HttpIo->ReqToken.Message->Body = Body;
-
- if (HttpIo->Callback != NULL) {
- Status = HttpIo->Callback (
- HttpIoRequest,
- HttpIo->ReqToken.Message,
- HttpIo->Context
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
-
- //
- // Queue the request token to HTTP instances.
- //
- Http = HttpIo->Http;
- HttpIo->IsTxDone = FALSE;
- Status = Http->Request (
- Http,
- &HttpIo->ReqToken
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- //
- // Poll the network until transmit finish.
- //
- while (!HttpIo->IsTxDone) {
- Http->Poll (Http);
- }
-
- return HttpIo->ReqToken.Status;
-}
-
-/**
- Synchronously receive a HTTP RESPONSE message from the server.
-
- @param[in] HttpIo The HttpIo wrapping the HTTP service.
- @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
- FALSE to continue receive the previous response message.
- @param[out] ResponseData Point to a wrapper of the received response data.
-
- @retval EFI_SUCCESS The HTTP response is received.
- @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
- @retval Others Other errors as indicated.
-
-**/
-EFI_STATUS
-HttpIoRecvResponse (
- IN HTTP_IO *HttpIo,
- IN BOOLEAN RecvMsgHeader,
- OUT HTTP_IO_RESPONSE_DATA *ResponseData
- )
-{
- EFI_STATUS Status;
- EFI_HTTP_PROTOCOL *Http;
-
- if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- //
- // Start the timer, and wait Timeout seconds to receive the header packet.
- //
- Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HTTP_BOOT_RESPONSE_TIMEOUT * TICKS_PER_MS);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- //
- // Queue the response token to HTTP instances.
- //
- HttpIo->RspToken.Status = EFI_NOT_READY;
- if (RecvMsgHeader) {
- HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
- } else {
- HttpIo->RspToken.Message->Data.Response = NULL;
- }
- HttpIo->RspToken.Message->HeaderCount = 0;
- HttpIo->RspToken.Message->Headers = NULL;
- HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;
- HttpIo->RspToken.Message->Body = ResponseData->Body;
-
- Http = HttpIo->Http;
- HttpIo->IsRxDone = FALSE;
- Status = Http->Response (
- Http,
- &HttpIo->RspToken
- );
-
- if (EFI_ERROR (Status)) {
- gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
- return Status;
- }
-
- //
- // Poll the network until receive finish.
- //
- while (!HttpIo->IsRxDone && ((HttpIo->TimeoutEvent == NULL) || EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent)))) {
- Http->Poll (Http);
- }
-
- gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
-
- if (!HttpIo->IsRxDone) {
- //
- // Timeout occurs, cancel the response token.
- //
- Http->Cancel (Http, &HttpIo->RspToken);
-
- Status = EFI_TIMEOUT;
-
- return Status;
- } else {
- HttpIo->IsRxDone = FALSE;
- }
-
- if ((HttpIo->Callback != NULL) &&
- (HttpIo->RspToken.Status == EFI_SUCCESS || HttpIo->RspToken.Status == EFI_HTTP_ERROR)) {
- Status = HttpIo->Callback (
- HttpIoResponse,
- HttpIo->RspToken.Message,
- HttpIo->Context
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
-
- //
- // Store the received data into the wrapper.
- //
- ResponseData->Status = HttpIo->RspToken.Status;
- ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
- ResponseData->Headers = HttpIo->RspToken.Message->Headers;
- ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;
-
- return Status;
-}
-
/**
This function checks the HTTP(S) URI scheme.

diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.h b/NetworkPkg/HttpBootDxe/HttpBootSupport.h
index 81d072ae37..1a2d32dd5a 100644
--- a/NetworkPkg/HttpBootDxe/HttpBootSupport.h
+++ b/NetworkPkg/HttpBootDxe/HttpBootSupport.h
@@ -2,6 +2,7 @@
Support functions declaration for UEFI HTTP boot driver.

Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/
@@ -139,102 +140,6 @@ HttpBootSetHeader (
IN CHAR8 *FieldValue
);

-///
-/// HTTP_IO_CALLBACK_EVENT
-///
-typedef enum {
- HttpIoRequest,
- HttpIoResponse
-} HTTP_IO_CALLBACK_EVENT;
-
-/**
- HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
-
- @param[in] EventType Indicate the Event type that occurs in the current callback.
- @param[in] Message HTTP message which will be send to, or just received from HTTP server.
- @param[in] Context The Callback Context pointer.
-
- @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
- @retval Others Tells the HttpIo to abort the current HTTP process.
-**/
-typedef
-EFI_STATUS
-(EFIAPI * HTTP_IO_CALLBACK) (
- IN HTTP_IO_CALLBACK_EVENT EventType,
- IN EFI_HTTP_MESSAGE *Message,
- IN VOID *Context
- );
-
-//
-// HTTP_IO configuration data for IPv4
-//
-typedef struct {
- EFI_HTTP_VERSION HttpVersion;
- UINT32 RequestTimeOut; // In milliseconds.
- UINT32 ResponseTimeOut; // In milliseconds.
- BOOLEAN UseDefaultAddress;
- EFI_IPv4_ADDRESS LocalIp;
- EFI_IPv4_ADDRESS SubnetMask;
- UINT16 LocalPort;
-} HTTP4_IO_CONFIG_DATA;
-
-//
-// HTTP_IO configuration data for IPv6
-//
-typedef struct {
- EFI_HTTP_VERSION HttpVersion;
- UINT32 RequestTimeOut; // In milliseconds.
- BOOLEAN UseDefaultAddress;
- EFI_IPv6_ADDRESS LocalIp;
- UINT16 LocalPort;
-} HTTP6_IO_CONFIG_DATA;
-
-
-//
-// HTTP_IO configuration
-//
-typedef union {
- HTTP4_IO_CONFIG_DATA Config4;
- HTTP6_IO_CONFIG_DATA Config6;
-} HTTP_IO_CONFIG_DATA;
-
-//
-// HTTP_IO wrapper of the EFI HTTP service.
-//
-typedef struct {
- UINT8 IpVersion;
- EFI_HANDLE Image;
- EFI_HANDLE Controller;
- EFI_HANDLE Handle;
-
- EFI_HTTP_PROTOCOL *Http;
-
- HTTP_IO_CALLBACK Callback;
- VOID *Context;
-
- EFI_HTTP_TOKEN ReqToken;
- EFI_HTTP_MESSAGE ReqMessage;
- EFI_HTTP_TOKEN RspToken;
- EFI_HTTP_MESSAGE RspMessage;
-
- BOOLEAN IsTxDone;
- BOOLEAN IsRxDone;
-
- EFI_EVENT TimeoutEvent;
-} HTTP_IO;
-
-//
-// A wrapper structure to hold the received HTTP response data.
-//
-typedef struct {
- EFI_HTTP_RESPONSE_DATA Response;
- UINTN HeaderCount;
- EFI_HTTP_HEADER *Headers;
- UINTN BodyLength;
- CHAR8 *Body;
- EFI_STATUS Status;
-} HTTP_IO_RESPONSE_DATA;
-
/**
Retrieve the host address using the EFI_DNS6_PROTOCOL.

@@ -267,98 +172,6 @@ HttpBootCommonNotify (
IN VOID *Context
);

-/**
- Create a HTTP_IO to access the HTTP service. It will create and configure
- a HTTP child handle.
-
- @param[in] Image The handle of the driver image.
- @param[in] Controller The handle of the controller.
- @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
- @param[in] ConfigData The HTTP_IO configuration data.
- @param[in] Callback Callback function which will be invoked when specified
- HTTP_IO_CALLBACK_EVENT happened.
- @param[in] Context The Context data which will be passed to the Callback function.
- @param[out] HttpIo The HTTP_IO.
-
- @retval EFI_SUCCESS The HTTP_IO is created and configured.
- @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
- @retval EFI_UNSUPPORTED One or more of the control options are not
- supported in the implementation.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- @retval Others Failed to create the HTTP_IO or configure it.
-
-**/
-EFI_STATUS
-HttpIoCreateIo (
- IN EFI_HANDLE Image,
- IN EFI_HANDLE Controller,
- IN UINT8 IpVersion,
- IN HTTP_IO_CONFIG_DATA *ConfigData,
- IN HTTP_IO_CALLBACK Callback,
- IN VOID *Context,
- OUT HTTP_IO *HttpIo
- );
-
-/**
- Destroy the HTTP_IO and release the resources.
-
- @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
-
-**/
-VOID
-HttpIoDestroyIo (
- IN HTTP_IO *HttpIo
- );
-
-/**
- Synchronously send a HTTP REQUEST message to the server.
-
- @param[in] HttpIo The HttpIo wrapping the HTTP service.
- @param[in] Request A pointer to storage such data as URL and HTTP method.
- @param[in] HeaderCount Number of HTTP header structures in Headers list.
- @param[in] Headers Array containing list of HTTP headers.
- @param[in] BodyLength Length in bytes of the HTTP body.
- @param[in] Body Body associated with the HTTP request.
-
- @retval EFI_SUCCESS The HTTP request is transmitted.
- @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
- @retval Others Other errors as indicated.
-
-**/
-EFI_STATUS
-HttpIoSendRequest (
- IN HTTP_IO *HttpIo,
- IN EFI_HTTP_REQUEST_DATA *Request, OPTIONAL
- IN UINTN HeaderCount,
- IN EFI_HTTP_HEADER *Headers, OPTIONAL
- IN UINTN BodyLength,
- IN VOID *Body OPTIONAL
- );
-
-/**
- Synchronously receive a HTTP RESPONSE message from the server.
-
- @param[in] HttpIo The HttpIo wrapping the HTTP service.
- @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
- FALSE to continue receive the previous response message.
- @param[out] ResponseData Point to a wrapper of the received response data.
-
- @retval EFI_SUCCESS The HTTP response is received.
- @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
- @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
- @retval Others Other errors as indicated.
-
-**/
-EFI_STATUS
-HttpIoRecvResponse (
- IN HTTP_IO *HttpIo,
- IN BOOLEAN RecvMsgHeader,
- OUT HTTP_IO_RESPONSE_DATA *ResponseData
- );
-
/**
This function checks the HTTP(S) URI scheme.

--
2.17.1


[DxeHttpIoLib PATCH V4 1/3] NetworkPkg/Library: Implementation of Http IO Helper Library

Abner Chang
 

Add HTTP IO helper library which could be used by HTTP applications
such as HTTP Boot, Redfish HTTP REST EX driver instance and etc.

Signed-off-by: Abner Chang <abner.chang@...>

Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Nickle Wang <nickle.wang@...>
---
NetworkPkg/Include/Library/HttpIoLib.h | 328 +++++++
.../Library/DxeHttpIoLib/DxeHttpIoLib.c | 847 ++++++++++++++++++
.../Library/DxeHttpIoLib/DxeHttpIoLib.inf | 43 +
.../Library/DxeHttpIoLib/DxeHttpIoLib.uni | 13 +
4 files changed, 1231 insertions(+)
create mode 100644 NetworkPkg/Include/Library/HttpIoLib.h
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni

diff --git a/NetworkPkg/Include/Library/HttpIoLib.h b/NetworkPkg/Include/Library/HttpIoLib.h
new file mode 100644
index 0000000000..8f3804ca42
--- /dev/null
+++ b/NetworkPkg/Include/Library/HttpIoLib.h
@@ -0,0 +1,328 @@
+/** @file
+ HttpIoLib.h.
+
+(C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef HTTP_IO_LIB_H_
+#define HTTP_IO_LIB_H_
+
+#include <IndustryStandard/Http11.h>
+
+#include <Library/DpcLib.h>
+#include <Library/HttpLib.h>
+#include <Library/NetLib.h>
+
+#define HTTP_IO_MAX_SEND_PAYLOAD 1024
+#define HTTP_IO_CHUNK_SIZE_STRING_LEN 50
+#define HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH 256
+
+///
+/// HTTP_IO_CALLBACK_EVENT
+///
+typedef enum {
+ HttpIoRequest,
+ HttpIoResponse
+} HTTP_IO_CALLBACK_EVENT;
+
+/**
+ HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
+
+ @param[in] EventType Indicate the Event type that occurs in the current callback.
+ @param[in] Message HTTP message which will be send to, or just received from HTTP server.
+ @param[in] Context The Callback Context pointer.
+
+ @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
+ @retval Others Tells the HttpIo to abort the current HTTP process.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * HTTP_IO_CALLBACK) (
+ IN HTTP_IO_CALLBACK_EVENT EventType,
+ IN EFI_HTTP_MESSAGE *Message,
+ IN VOID *Context
+ );
+
+///
+/// A wrapper structure to hold the received HTTP response data.
+///
+typedef struct {
+ EFI_HTTP_RESPONSE_DATA Response;
+ UINTN HeaderCount;
+ EFI_HTTP_HEADER *Headers;
+ UINTN BodyLength;
+ CHAR8 *Body;
+ EFI_STATUS Status;
+} HTTP_IO_RESPONSE_DATA;
+
+///
+/// HTTP_IO configuration data for IPv4
+///
+typedef struct {
+ EFI_HTTP_VERSION HttpVersion;
+ UINT32 RequestTimeOut; ///< In milliseconds.
+ UINT32 ResponseTimeOut; ///< In milliseconds.
+ BOOLEAN UseDefaultAddress;
+ EFI_IPv4_ADDRESS LocalIp;
+ EFI_IPv4_ADDRESS SubnetMask;
+ UINT16 LocalPort;
+} HTTP4_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO configuration data for IPv6
+///
+typedef struct {
+ EFI_HTTP_VERSION HttpVersion;
+ UINT32 RequestTimeOut; ///< In milliseconds.
+ BOOLEAN UseDefaultAddress;
+ EFI_IPv6_ADDRESS LocalIp;
+ UINT16 LocalPort;
+} HTTP6_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO configuration
+///
+typedef union {
+ HTTP4_IO_CONFIG_DATA Config4;
+ HTTP6_IO_CONFIG_DATA Config6;
+} HTTP_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO wrapper of the EFI HTTP service.
+///
+typedef struct {
+ UINT8 IpVersion;
+ EFI_HANDLE Image;
+ EFI_HANDLE Controller;
+ EFI_HANDLE Handle;
+
+ EFI_HTTP_PROTOCOL *Http;
+
+ HTTP_IO_CALLBACK Callback;
+ VOID *Context;
+
+ EFI_HTTP_TOKEN ReqToken;
+ EFI_HTTP_MESSAGE ReqMessage;
+ EFI_HTTP_TOKEN RspToken;
+ EFI_HTTP_MESSAGE RspMessage;
+
+ BOOLEAN IsTxDone;
+ BOOLEAN IsRxDone;
+
+ EFI_EVENT TimeoutEvent;
+ UINT32 Timeout;
+} HTTP_IO;
+
+///
+/// Process code of HTTP chunk transfer.
+///
+typedef enum {
+ HttpIoSendChunkNone = 0,
+ HttpIoSendChunkHeaderZeroContent,
+ HttpIoSendChunkContent,
+ HttpIoSendChunkEndChunk,
+ HttpIoSendChunkFinish
+} HTTP_IO_SEND_CHUNK_PROCESS;
+
+///
+/// Process code of HTTP non chunk transfer.
+///
+typedef enum {
+ HttpIoSendNonChunkNone = 0,
+ HttpIoSendNonChunkHeaderZeroContent,
+ HttpIoSendNonChunkContent,
+ HttpIoSendNonChunkFinish
+} HTTP_IO_SEND_NON_CHUNK_PROCESS;
+
+///
+/// Chunk links for HTTP chunked transfer coding.
+///
+typedef struct {
+ LIST_ENTRY NextChunk;
+ UINTN Length;
+ CHAR8 *Data;
+} HTTP_IO_CHUNKS;
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotifyDpc (
+ IN VOID *Context
+ );
+
+/**
+ Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Destroy the HTTP_IO and release the resources.
+
+ @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
+
+**/
+VOID
+HttpIoDestroyIo (
+ IN HTTP_IO *HttpIo
+ );
+
+/**
+ Create a HTTP_IO to access the HTTP service. It will create and configure
+ a HTTP child handle.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
+ @param[in] ConfigData The HTTP_IO configuration data.
+ @param[in] Callback Callback function which will be invoked when specified
+ HTTP_IO_CALLBACK_EVENT happened.
+ @param[in] Context The Context data which will be passed to the Callback function.
+ @param[out] HttpIo The HTTP_IO.
+
+ @retval EFI_SUCCESS The HTTP_IO is created and configured.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Failed to create the HTTP_IO or configure it.
+
+**/
+EFI_STATUS
+HttpIoCreateIo (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN UINT8 IpVersion,
+ IN HTTP_IO_CONFIG_DATA *ConfigData,
+ IN HTTP_IO_CALLBACK Callback,
+ IN VOID *Context,
+ OUT HTTP_IO *HttpIo
+ );
+
+/**
+ Synchronously send a HTTP REQUEST message to the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] Request A pointer to storage such data as URL and HTTP method.
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[in] BodyLength Length in bytes of the HTTP body.
+ @param[in] Body Body associated with the HTTP request.
+
+ @retval EFI_SUCCESS The HTTP request is transmitted.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoSendRequest (
+ IN HTTP_IO *HttpIo,
+ IN EFI_HTTP_REQUEST_DATA *Request, OPTIONAL
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers, OPTIONAL
+ IN UINTN BodyLength,
+ IN VOID *Body OPTIONAL
+ );
+
+/**
+ Synchronously receive a HTTP RESPONSE message from the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
+ FALSE to continue receive the previous response message.
+ @param[out] ResponseData Point to a wrapper of the received response data.
+
+ @retval EFI_SUCCESS The HTTP response is received.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoRecvResponse (
+ IN HTTP_IO *HttpIo,
+ IN BOOLEAN RecvMsgHeader,
+ OUT HTTP_IO_RESPONSE_DATA *ResponseData
+ );
+
+/**
+ Get the value of the content length if there is a "Content-Length" header.
+
+ @param[in] HeaderCount Number of HTTP header structures in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ContentLength Pointer to save the value of the content length.
+
+ @retval EFI_SUCCESS Successfully get the content length.
+ @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
+
+**/
+EFI_STATUS
+HttpIoGetContentLength (
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT UINTN *ContentLength
+ );
+
+/**
+ Synchronously receive a HTTP RESPONSE message from the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] HeaderCount Number of headers in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ChunkListHead A pointer to receivce list head of chunked data.
+ Caller has to release memory of ChunkListHead
+ and all list entries.
+ @param[out] ContentLength Total content length
+
+ @retval EFI_SUCCESS The HTTP chunked transfer is received.
+ @retval EFI_NOT_FOUND No chunked transfer coding header found.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_INVALID_PARAMETER Improper parameters.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoGetChunkedTransferContent (
+ IN HTTP_IO *HttpIo,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT LIST_ENTRY **ChunkListHead,
+ OUT UINTN *ContentLength
+ );
+
+/**
+ Send HTTP request in chunks.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] SendChunkProcess Pointer to current chunk process status.
+ @param[out] RequestMessage Request to send.
+
+ @retval EFI_SUCCESS Successfully to send chunk data according to SendChunkProcess.
+ @retval Other Other errors.
+
+**/
+EFI_STATUS
+HttpIoSendChunkedTransfer (
+ IN HTTP_IO *HttpIo,
+ IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,
+ IN EFI_HTTP_MESSAGE *RequestMessage
+);
+#endif
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
new file mode 100644
index 0000000000..250477965c
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
@@ -0,0 +1,847 @@
+/** @file
+ Http IO Helper Library.
+
+ (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/Http.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HttpIoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotifyDpc (
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK
+ //
+ QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, Context);
+}
+
+/**
+ Destroy the HTTP_IO and release the resources.
+
+ @param[in] HttpIo The HTTP_IO which wraps the HTTP service to be destroyed.
+
+**/
+VOID
+HttpIoDestroyIo (
+ IN HTTP_IO *HttpIo
+ )
+{
+ EFI_HTTP_PROTOCOL *Http;
+ EFI_EVENT Event;
+
+ if (HttpIo == NULL) {
+ return;
+ }
+
+ Event = HttpIo->ReqToken.Event;
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Event = HttpIo->RspToken.Event;
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Event = HttpIo->TimeoutEvent;
+ if (Event != NULL) {
+ gBS->CloseEvent (Event);
+ }
+
+ Http = HttpIo->Http;
+ if (Http != NULL) {
+ Http->Configure (Http, NULL);
+ gBS->CloseProtocol (
+ HttpIo->Handle,
+ &gEfiHttpProtocolGuid,
+ HttpIo->Image,
+ HttpIo->Controller
+ );
+ }
+
+ NetLibDestroyServiceChild (
+ HttpIo->Controller,
+ HttpIo->Image,
+ &gEfiHttpServiceBindingProtocolGuid,
+ HttpIo->Handle
+ );
+}
+
+/**
+ Create a HTTP_IO to access the HTTP service. It will create and configure
+ a HTTP child handle.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
+ @param[in] ConfigData The HTTP_IO configuration data ,
+ NULL means not to configure the HTTP child.
+ @param[in] Callback Callback function which will be invoked when specified
+ HTTP_IO_CALLBACK_EVENT happened.
+ @param[in] Context The Context data which will be passed to the Callback function.
+ @param[out] HttpIo The HTTP_IO.
+
+ @retval EFI_SUCCESS The HTTP_IO is created and configured.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Failed to create the HTTP_IO or configure it.
+
+**/
+EFI_STATUS
+HttpIoCreateIo (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN UINT8 IpVersion,
+ IN HTTP_IO_CONFIG_DATA *ConfigData, OPTIONAL
+ IN HTTP_IO_CALLBACK Callback,
+ IN VOID *Context,
+ OUT HTTP_IO *HttpIo
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_CONFIG_DATA HttpConfigData;
+ EFI_HTTPv4_ACCESS_POINT Http4AccessPoint;
+ EFI_HTTPv6_ACCESS_POINT Http6AccessPoint;
+ EFI_HTTP_PROTOCOL *Http;
+ EFI_EVENT Event;
+
+ if ((Image == NULL) || (Controller == NULL) || (HttpIo == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
+ return EFI_UNSUPPORTED;
+ }
+
+ ZeroMem (HttpIo, sizeof (HTTP_IO));
+ ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
+
+ //
+ // Create the HTTP child instance and get the HTTP protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ Image,
+ &gEfiHttpServiceBindingProtocolGuid,
+ &HttpIo->Handle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ HttpIo->Handle,
+ &gEfiHttpProtocolGuid,
+ (VOID **) &Http,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status) || (Http == NULL)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Init the configuration data and configure the HTTP child.
+ //
+ HttpIo->Image = Image;
+ HttpIo->Controller = Controller;
+ HttpIo->IpVersion = IpVersion;
+ HttpIo->Http = Http;
+ HttpIo->Callback = Callback;
+ HttpIo->Context = Context;
+
+ if (ConfigData != NULL) {
+ if (HttpIo->IpVersion == IP_VERSION_4) {
+ HttpConfigData.LocalAddressIsIPv6 = FALSE;
+ HttpConfigData.HttpVersion = ConfigData->Config4.HttpVersion;
+ HttpConfigData.TimeOutMillisec = ConfigData->Config4.RequestTimeOut;
+
+ Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
+ Http4AccessPoint.LocalPort = ConfigData->Config4.LocalPort;
+ IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
+ IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
+ HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
+ } else {
+ HttpConfigData.LocalAddressIsIPv6 = TRUE;
+ HttpConfigData.HttpVersion = ConfigData->Config6.HttpVersion;
+ HttpConfigData.TimeOutMillisec = ConfigData->Config6.RequestTimeOut;
+
+ Http6AccessPoint.LocalPort = ConfigData->Config6.LocalPort;
+ IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);
+ HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
+ }
+
+ Status = Http->Configure (Http, &HttpConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Create events for variuos asynchronous operations.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpIoNotify,
+ &HttpIo->IsTxDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ HttpIo->ReqToken.Event = Event;
+ HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpIoNotify,
+ &HttpIo->IsRxDone,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ HttpIo->RspToken.Event = Event;
+ HttpIo->RspToken.Message = &HttpIo->RspMessage;
+
+ //
+ // Create TimeoutEvent for response
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ HttpIo->TimeoutEvent = Event;
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ HttpIoDestroyIo (HttpIo);
+
+ return Status;
+}
+
+/**
+ Synchronously send a HTTP REQUEST message to the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] Request A pointer to storage such data as URL and HTTP method.
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[in] BodyLength Length in bytes of the HTTP body.
+ @param[in] Body Body associated with the HTTP request.
+
+ @retval EFI_SUCCESS The HTTP request is trasmitted.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoSendRequest (
+ IN HTTP_IO *HttpIo,
+ IN EFI_HTTP_REQUEST_DATA *Request,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ IN UINTN BodyLength,
+ IN VOID *Body
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_PROTOCOL *Http;
+
+ if (HttpIo == NULL || HttpIo->Http == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HttpIo->ReqToken.Status = EFI_NOT_READY;
+ HttpIo->ReqToken.Message->Data.Request = Request;
+ HttpIo->ReqToken.Message->HeaderCount = HeaderCount;
+ HttpIo->ReqToken.Message->Headers = Headers;
+ HttpIo->ReqToken.Message->BodyLength = BodyLength;
+ HttpIo->ReqToken.Message->Body = Body;
+
+ if (HttpIo->Callback != NULL) {
+ Status = HttpIo->Callback (
+ HttpIoRequest,
+ HttpIo->ReqToken.Message,
+ HttpIo->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Queue the request token to HTTP instances.
+ //
+ Http = HttpIo->Http;
+ HttpIo->IsTxDone = FALSE;
+ Status = Http->Request (
+ Http,
+ &HttpIo->ReqToken
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Poll the network until transmit finish.
+ //
+ while (!HttpIo->IsTxDone) {
+ Http->Poll (Http);
+ }
+
+ return HttpIo->ReqToken.Status;
+}
+
+/**
+ Synchronously receive a HTTP RESPONSE message from the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] RecvMsgHeader TRUE to receive a new HTTP response (from message header).
+ FALSE to continue receive the previous response message.
+ @param[out] ResponseData Point to a wrapper of the received response data.
+
+ @retval EFI_SUCCESS The HTTP response is received.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoRecvResponse (
+ IN HTTP_IO *HttpIo,
+ IN BOOLEAN RecvMsgHeader,
+ OUT HTTP_IO_RESPONSE_DATA *ResponseData
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_PROTOCOL *Http;
+
+ if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Queue the response token to HTTP instances.
+ //
+ HttpIo->RspToken.Status = EFI_NOT_READY;
+ if (RecvMsgHeader) {
+ HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
+ } else {
+ HttpIo->RspToken.Message->Data.Response = NULL;
+ }
+ HttpIo->RspToken.Message->HeaderCount = 0;
+ HttpIo->RspToken.Message->Headers = NULL;
+ HttpIo->RspToken.Message->BodyLength = ResponseData->BodyLength;
+ HttpIo->RspToken.Message->Body = ResponseData->Body;
+
+ Http = HttpIo->Http;
+ HttpIo->IsRxDone = FALSE;
+
+ //
+ // Start the timer, and wait Timeout seconds to receive the header packet.
+ //
+ Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HttpIo->Timeout * TICKS_PER_MS);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Http->Response (
+ Http,
+ &HttpIo->RspToken
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Remove timeout timer from the event list.
+ //
+ gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
+ return Status;
+ }
+
+ //
+ // Poll the network until receive finish.
+ //
+ while (!HttpIo->IsRxDone && EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent))) {
+ Http->Poll (Http);
+ }
+
+ //
+ // Remove timeout timer from the event list.
+ //
+ gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
+
+ if (!HttpIo->IsRxDone) {
+ //
+ // Timeout occurs, cancel the response token.
+ //
+ Http->Cancel (Http, &HttpIo->RspToken);
+
+ Status = EFI_TIMEOUT;
+
+ return Status;
+ } else {
+ HttpIo->IsRxDone = FALSE;
+ }
+
+ if ((HttpIo->Callback != NULL) &&
+ (HttpIo->RspToken.Status == EFI_SUCCESS || HttpIo->RspToken.Status == EFI_HTTP_ERROR)) {
+ Status = HttpIo->Callback (
+ HttpIoResponse,
+ HttpIo->RspToken.Message,
+ HttpIo->Context
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Store the received data into the wrapper.
+ //
+ ResponseData->Status = HttpIo->RspToken.Status;
+ ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
+ ResponseData->Headers = HttpIo->RspToken.Message->Headers;
+ ResponseData->BodyLength = HttpIo->RspToken.Message->BodyLength;
+
+ return Status;
+}
+
+/**
+ Get the value of the content length if there is a "Content-Length" header.
+
+ @param[in] HeaderCount Number of HTTP header structures in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ContentLength Pointer to save the value of the content length.
+
+ @retval EFI_SUCCESS Successfully get the content length.
+ @retval EFI_NOT_FOUND No "Content-Length" header in the Headers.
+
+**/
+EFI_STATUS
+HttpIoGetContentLength (
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT UINTN *ContentLength
+ )
+{
+ EFI_HTTP_HEADER *Header;
+
+ Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);
+ if (Header == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength);
+}
+/**
+ Send HTTP request in chunks.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] SendChunkProcess Pointer to current chunk process status.
+ @param[in] RequestMessage Request to send.
+
+ @retval EFI_SUCCESS Successfully to send chunk data according to SendChunkProcess.
+ @retval Other Other errors.
+
+**/
+EFI_STATUS
+HttpIoSendChunkedTransfer (
+ IN HTTP_IO *HttpIo,
+ IN HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,
+ IN EFI_HTTP_MESSAGE *RequestMessage
+)
+{
+ EFI_STATUS Status;
+ EFI_HTTP_HEADER *NewHeaders;
+ EFI_HTTP_HEADER *ContentLengthHeader;
+ UINTN AddNewHeader;
+ UINTN HeaderCount;
+ CHAR8 *MessageBody;
+ UINTN MessageBodyLength;
+ UINTN ChunkLength;
+ CHAR8 ChunkLengthStr [HTTP_IO_CHUNK_SIZE_STRING_LEN];
+ EFI_HTTP_REQUEST_DATA *SentRequestData;
+
+ AddNewHeader = 0;
+ NewHeaders = NULL;
+ MessageBody = NULL;
+ ContentLengthHeader = NULL;
+ MessageBodyLength = 0;
+
+ switch (*SendChunkProcess) {
+ case HttpIoSendChunkHeaderZeroContent:
+ ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, RequestMessage->Headers, HTTP_HEADER_CONTENT_LENGTH);
+ if (ContentLengthHeader == NULL) {
+ AddNewHeader = 1;
+ }
+
+ NewHeaders = AllocateZeroPool ((RequestMessage->HeaderCount + AddNewHeader) * sizeof(EFI_HTTP_HEADER));
+ CopyMem ((VOID*)NewHeaders, (VOID *)RequestMessage->Headers, RequestMessage->HeaderCount * sizeof (EFI_HTTP_HEADER));
+ if (AddNewHeader == 0) {
+ //
+ // Override content-length to Transfer-Encoding.
+ //
+ ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, NewHeaders, HTTP_HEADER_CONTENT_LENGTH);
+ ContentLengthHeader->FieldName = NULL;
+ ContentLengthHeader->FieldValue = NULL;
+ } else {
+ ContentLengthHeader = NewHeaders + RequestMessage->HeaderCount;
+ }
+ HttpSetFieldNameAndValue (ContentLengthHeader, HTTP_HEADER_TRANSFER_ENCODING, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED);
+ HeaderCount = RequestMessage->HeaderCount + AddNewHeader;
+ MessageBodyLength = 0;
+ MessageBody = NULL;
+ SentRequestData = RequestMessage->Data.Request;
+ break;
+
+ case HttpIoSendChunkContent:
+ HeaderCount = 0;
+ NewHeaders = NULL;
+ SentRequestData = NULL;
+ if (RequestMessage->BodyLength > HTTP_IO_MAX_SEND_PAYLOAD) {
+ MessageBodyLength = HTTP_IO_MAX_SEND_PAYLOAD;
+ } else {
+ MessageBodyLength = RequestMessage->BodyLength;
+ }
+ AsciiSPrint (
+ ChunkLengthStr,
+ HTTP_IO_CHUNK_SIZE_STRING_LEN,
+ "%x%c%c",
+ MessageBodyLength,
+ CHUNKED_TRANSFER_CODING_CR,
+ CHUNKED_TRANSFER_CODING_LF
+ );
+ ChunkLength = AsciiStrLen (ChunkLengthStr);
+ MessageBody = AllocatePool (ChunkLength + MessageBodyLength + 2);
+ if (MessageBody == NULL) {
+ DEBUG((DEBUG_ERROR, "Not enough memory for chunk transfer\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Build up the chunk transfer paylaod.
+ //
+ CopyMem (MessageBody, ChunkLengthStr, ChunkLength);
+ CopyMem (MessageBody + ChunkLength, RequestMessage->Body, MessageBodyLength);
+ *(MessageBody + ChunkLength + MessageBodyLength) = CHUNKED_TRANSFER_CODING_CR;
+ *(MessageBody + ChunkLength + MessageBodyLength + 1) = CHUNKED_TRANSFER_CODING_LF;
+ //
+ // Change variables for the next chunk trasnfer.
+ //
+ RequestMessage->BodyLength -= MessageBodyLength;
+ RequestMessage->Body = (VOID *)((CHAR8 *)RequestMessage->Body + MessageBodyLength);
+ MessageBodyLength += (ChunkLength + 2);
+ if (RequestMessage->BodyLength == 0) {
+ *SendChunkProcess = HttpIoSendChunkEndChunk;
+ }
+ break;
+
+ case HttpIoSendChunkEndChunk:
+ HeaderCount = 0;
+ NewHeaders = NULL;
+ SentRequestData = NULL;
+ AsciiSPrint (
+ ChunkLengthStr,
+ HTTP_IO_CHUNK_SIZE_STRING_LEN,
+ "0%c%c%c%c",
+ CHUNKED_TRANSFER_CODING_CR,
+ CHUNKED_TRANSFER_CODING_LF,
+ CHUNKED_TRANSFER_CODING_CR,
+ CHUNKED_TRANSFER_CODING_LF
+ );
+ MessageBody = AllocatePool (AsciiStrLen(ChunkLengthStr));
+ if (MessageBody == NULL) {
+ DEBUG((DEBUG_ERROR, "Not enough memory for the end chunk transfer\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen (ChunkLengthStr));
+ MessageBodyLength = AsciiStrLen (ChunkLengthStr);
+ *SendChunkProcess = HttpIoSendChunkFinish;
+ break;
+
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = HttpIoSendRequest (
+ HttpIo,
+ SentRequestData,
+ HeaderCount,
+ NewHeaders,
+ MessageBodyLength,
+ MessageBody
+ );
+ if (ContentLengthHeader != NULL) {
+ if (ContentLengthHeader->FieldName != NULL) {
+ FreePool (ContentLengthHeader->FieldName);
+ }
+ if (ContentLengthHeader->FieldValue != NULL) {
+ FreePool (ContentLengthHeader->FieldValue);
+ }
+ }
+ if (NewHeaders != NULL) {
+ FreePool (NewHeaders);
+ }
+ if (MessageBody != NULL) {
+ FreePool (MessageBody);
+ }
+ return Status;
+}
+
+/**
+ Synchronously receive a HTTP RESPONSE message from the server.
+
+ @param[in] HttpIo The HttpIo wrapping the HTTP service.
+ @param[in] HeaderCount Number of headers in Headers.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ChunkListHead A pointer to receive list head
+ of chunked data. Caller has to
+ release memory of ChunkListHead
+ and all list entries.
+ @param[out] ContentLength Total content length
+
+ @retval EFI_SUCCESS The HTTP chunked transfer is received.
+ @retval EFI_NOT_FOUND No chunked transfer coding header found.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_INVALID_PARAMETER Improper parameters.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoGetChunkedTransferContent (
+ IN HTTP_IO *HttpIo,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT LIST_ENTRY **ChunkListHead,
+ OUT UINTN *ContentLength
+ )
+{
+ EFI_HTTP_HEADER *Header;
+ CHAR8 ChunkSizeAscii [256];
+ EFI_STATUS Status;
+ UINTN Index;
+ HTTP_IO_RESPONSE_DATA ResponseData;
+ UINTN TotalLength;
+ LIST_ENTRY *HttpChunks;
+ HTTP_IO_CHUNKS *ThisChunk;
+ LIST_ENTRY *ThisListEntry;
+
+ if (ChunkListHead == NULL || ContentLength == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *ContentLength = 0;
+ Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);
+ if (Header == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (AsciiStrCmp (Header->FieldValue, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED) != 0) {
+ return EFI_NOT_FOUND;
+ }
+ //
+ // Loop to get all chunks.
+ //
+ TotalLength = 0;
+ HttpChunks = (LIST_ENTRY *)AllocateZeroPool (sizeof (LIST_ENTRY));
+ if (HttpChunks == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ InitializeListHead (HttpChunks);
+ DEBUG ((DEBUG_INFO, " Chunked transfer\n"));
+ while (TRUE) {
+ ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA));
+ ResponseData.BodyLength = HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH;
+ ResponseData.Body = ChunkSizeAscii;
+ Status = HttpIoRecvResponse (
+ HttpIo,
+ FALSE,
+ &ResponseData
+ );
+ if (EFI_ERROR (Status)) {
+ goto ExitDeleteChunks;
+ }
+ //
+ // Decoding Chunked Transfer Coding.
+ // Only decode chunk-size and last chunk.
+ //
+ DEBUG ((DEBUG_INFO, " Chunk HTTP Response StatusCode - %d\n", ResponseData.Response.StatusCode));
+ //
+ // Break if this is last chunk.
+ //
+ if (ChunkSizeAscii [0] == CHUNKED_TRANSFER_CODING_LAST_CHUNK) {
+ //
+ // Check if this is a valid Last-Chunk.
+ //
+ if ((ChunkSizeAscii [1] != CHUNKED_TRANSFER_CODING_CR) ||
+ (ChunkSizeAscii [2] != CHUNKED_TRANSFER_CODING_LF)
+ ) {
+ DEBUG ((DEBUG_ERROR, " This is an invalid Last-chunk\n"));
+ Status = EFI_INVALID_PARAMETER;
+ goto ExitDeleteChunks;
+ }
+
+ Status = EFI_SUCCESS;
+ DEBUG ((DEBUG_INFO, " Last-chunk\n"));
+ ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));
+ if (ThisChunk == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+
+ InitializeListHead (&ThisChunk->NextChunk);
+ ThisChunk->Length = ResponseData.BodyLength - 1 - 2; // Minus sizeof '0' and CRLF.
+ ThisChunk->Data = (CHAR8 *)AllocatePool (ThisChunk->Length);
+ if (ThisChunk->Data == NULL) {
+ FreePool ((UINT8 *)ThisChunk);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ CopyMem ((UINT8 *)ThisChunk->Data, (UINT8 *)ResponseData.Body + 1, ThisChunk->Length);
+ TotalLength += ThisChunk->Length;
+ InsertTailList (HttpChunks, &ThisChunk->NextChunk);
+ break;
+ }
+
+ //
+ // Get the chunk length
+ //
+ Index = 0;
+ while ((ChunkSizeAscii [Index] != CHUNKED_TRANSFER_CODING_EXTENSION_SEPARATOR) &&
+ (ChunkSizeAscii [Index] != (CHAR8)CHUNKED_TRANSFER_CODING_CR) &&
+ (Index != HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH)) {
+ Index ++;
+ }
+
+ if (Index == HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH) {
+ Status = EFI_NOT_FOUND;
+ goto ExitDeleteChunks;
+ }
+ ChunkSizeAscii[Index] = 0;
+ AsciiStrHexToUintnS (ChunkSizeAscii, NULL, ContentLength);
+ DEBUG ((DEBUG_INFO, " Length of this chunk %d\n", *ContentLength));
+ //
+ // Receive the data;
+ //
+ ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));
+ if (ThisChunk == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ ResponseData.BodyLength = *ContentLength;
+ ResponseData.Body = (CHAR8 *)AllocatePool (*ContentLength);
+ if (ResponseData.Body == NULL) {
+ FreePool (ThisChunk);
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ExitDeleteChunks;
+ }
+ InitializeListHead (&ThisChunk->NextChunk);
+ ThisChunk->Length = *ContentLength;
+ ThisChunk->Data = ResponseData.Body;
+ InsertTailList (HttpChunks, &ThisChunk->NextChunk);
+ Status = HttpIoRecvResponse (
+ HttpIo,
+ FALSE,
+ &ResponseData
+ );
+ if (EFI_ERROR (Status)) {
+ goto ExitDeleteChunks;
+ }
+ //
+ // Read CRLF
+ //
+ ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA));
+ ResponseData.BodyLength = 2;
+ ResponseData.Body = ChunkSizeAscii;
+ Status = HttpIoRecvResponse (
+ HttpIo,
+ FALSE,
+ &ResponseData
+ );
+ if (EFI_ERROR (Status)) {
+ goto ExitDeleteChunks;
+ }
+ //
+ // Verify the end of chunk payload.
+ //
+ if ((ChunkSizeAscii [0] != CHUNKED_TRANSFER_CODING_CR) ||
+ (ChunkSizeAscii [1] != CHUNKED_TRANSFER_CODING_LF)
+ ) {
+ DEBUG ((DEBUG_ERROR, " This is an invalid End-of-chunk notation.\n"));
+ goto ExitDeleteChunks;
+ }
+ TotalLength += *ContentLength;
+ }
+
+ *ContentLength = TotalLength;
+ *ChunkListHead = HttpChunks;
+ DEBUG ((DEBUG_INFO, " Total of lengh of chunks :%d\n", TotalLength));
+ return EFI_SUCCESS;
+
+ExitDeleteChunks:
+ if (HttpChunks != NULL) {
+ while (!IsListEmpty(HttpChunks)) {
+ ThisListEntry = GetFirstNode (HttpChunks);
+ RemoveEntryList (ThisListEntry);
+ ThisChunk = (HTTP_IO_CHUNKS *)ThisListEntry;
+ if (ThisChunk->Data != NULL) {
+ FreePool (ThisChunk->Data);
+ }
+ FreePool(ThisListEntry);
+ }
+ FreePool (HttpChunks);
+ }
+ return Status;
+}
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
new file mode 100644
index 0000000000..a02b409547
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
@@ -0,0 +1,43 @@
+## @file
+# This library instance provides HTTP IO helper functions.
+#
+# (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001b
+ BASE_NAME = DxeHttpIoLib
+ MODULE_UNI_FILE = DxeHttpIoLib.uni
+ FILE_GUID = 50B198F8-7986-4F51-A857-CFE4643D59F3
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = HttpIoLib| DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC ARM AARM64 RISCV64
+#
+
+[Sources]
+ DxeHttpIoLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ NetworkPkg/NetworkPkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DpcLib
+ MemoryAllocationLib
+ PrintLib
+ UefiBootServicesTableLib
+
+[Protocols]
+ gEfiHttpProtocolGuid ## SOMETIMES_CONSUMES
+
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni
new file mode 100644
index 0000000000..d419b1a49d
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni
@@ -0,0 +1,13 @@
+// /** @file
+// The library instance provides HTTP IO helper functions.
+//
+// (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "HTTP IO Helper Library"
+
+#string STR_MODULE_DESCRIPTION #language en-US "The library instance provides HTTP IO helper functions."
+
--
2.17.1


[DxeHttpIoLib PATCH V4 2/3] NetworkPkg: Add Http IO Helper Library to NetworkPkg

Abner Chang
 

This library provides HTTP IO helper functions.

Signed-off-by: Abner Chang <abner.chang@...>

Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Nickle Wang <nickle.wang@...>
---
NetworkPkg/NetworkLibs.dsc.inc | 5 ++++-
NetworkPkg/NetworkPkg.dec | 6 +++++-
NetworkPkg/NetworkPkg.dsc | 1 +
3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/NetworkPkg/NetworkLibs.dsc.inc b/NetworkPkg/NetworkLibs.dsc.inc
index 4b99f48085..7cfc1a151a 100644
--- a/NetworkPkg/NetworkLibs.dsc.inc
+++ b/NetworkPkg/NetworkLibs.dsc.inc
@@ -6,6 +6,7 @@
# of EDKII network library classes.
#
# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -16,5 +17,7 @@
IpIoLib|NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.inf
UdpIoLib|NetworkPkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
TcpIoLib|NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.inf
- # HttpLib is used for Http Boot
+ # HttpLib and HttpIoLib are used for Http Boot and other
+ # HTTP applications.
HttpLib|NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf
+ HttpIoLib|NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
diff --git a/NetworkPkg/NetworkPkg.dec b/NetworkPkg/NetworkPkg.dec
index 66e500cbea..0449e1004a 100644
--- a/NetworkPkg/NetworkPkg.dec
+++ b/NetworkPkg/NetworkPkg.dec
@@ -4,7 +4,7 @@
# This package provides network modules that conform to UEFI 2.4 specification.
#
# Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
-# (C) Copyright 2015-2017 Hewlett Packard Enterprise Development LP<BR>
+# (C) Copyright 2015-2020 Hewlett Packard Enterprise Development LP<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -41,6 +41,10 @@
# This library is only intended to be used by UEFI network stack modules.
HttpLib|Include/Library/HttpLib.h

+ ## @libraryclass Http IO helper routines for HTTP transfer.
+ # This library is only intended to be used by UEFI network stack modules.
+ HttpIoLib|Include/Library/HttpIoLib.h
+
## @libraryclass Library for Deferred Procedure Calls.
DpcLib|Include/Library/DpcLib.h

diff --git a/NetworkPkg/NetworkPkg.dsc b/NetworkPkg/NetworkPkg.dsc
index 716d04fdad..216479a2f6 100644
--- a/NetworkPkg/NetworkPkg.dsc
+++ b/NetworkPkg/NetworkPkg.dsc
@@ -107,6 +107,7 @@
NetworkPkg/Application/VConfig/VConfig.inf
NetworkPkg/Library/DxeDpcLib/DxeDpcLib.inf
NetworkPkg/Library/DxeHttpLib/DxeHttpLib.inf
+ NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
NetworkPkg/Library/DxeIpIoLib/DxeIpIoLib.inf
NetworkPkg/Library/DxeNetLib/DxeNetLib.inf
NetworkPkg/Library/DxeTcpIoLib/DxeTcpIoLib.inf
--
2.17.1


[DxeHttpIoLib PATCH V4 0/3] Add Http IO Helper Library

Abner Chang
 

In v4: - Address the comments given by Maciej.
- Add checking of "\r\n" notation on the Last-chunk and
End-of-chunk.

In v3: Address the comments given by Maciej.

In v2: Revise HTTP Boot DXE to utilize HttpIoLib.
 
In order to leverage HTTP IO related functions implemented in HttpBootDxe
for edk2 Redfish REST EX HTTP driver instance, we would like to pull out
HTTP IO related functions from HttpBootDxe to a helper library under
network package.
 
This set of patches is for the new library HttpIoLib. HttpBootDxe is also
revised to utilize HttpIoLib. However we will need the owner of
HttpBootDxe to verify the functionality of HTTP boot becuase I don't
have the environment for that.
 
Signed-off-by: Abner Chang <abner.chang@...>
 
Cc: Maciej Rabeda <maciej.rabeda@...>
Cc: Jiaxin Wu <jiaxin.wu@...>
Cc: Siyuan Fu <siyuan.fu@...>
Cc: Nickle Wang <nickle.wang@...>

Abner Chang (3):
NetworkPkg/Library: Implementation of Http IO Helper Library
NetworkPkg: Add Http IO Helper Library to NetworkPkg
NetworkPkg/HttpBootDxe: Utilize HttpIoLib

NetworkPkg/HttpBootDxe/HttpBootDxe.h | 3 +-
NetworkPkg/HttpBootDxe/HttpBootDxe.inf | 2 +
NetworkPkg/HttpBootDxe/HttpBootSupport.c | 431 +--------
NetworkPkg/HttpBootDxe/HttpBootSupport.h | 189 +---
NetworkPkg/Include/Library/HttpIoLib.h | 328 +++++++
.../Library/DxeHttpIoLib/DxeHttpIoLib.c | 847 ++++++++++++++++++
.../Library/DxeHttpIoLib/DxeHttpIoLib.inf | 43 +
.../Library/DxeHttpIoLib/DxeHttpIoLib.uni | 13 +
NetworkPkg/NetworkLibs.dsc.inc | 5 +-
NetworkPkg/NetworkPkg.dec | 6 +-
NetworkPkg/NetworkPkg.dsc | 1 +
11 files changed, 1247 insertions(+), 621 deletions(-)
create mode 100644 NetworkPkg/Include/Library/HttpIoLib.h
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni

--
2.17.1


[PATCH v3 11/11] UefiCpuPkg/MpInitLib: For SEV-ES guest, set stack based on processor number

Lendacky, Thomas
 

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

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

Set the SEV-ES reset stack address for an AP based on the processor number
instead of the APIC ID in case the APIC IDs are not zero-based and densely
packed/enumerated. This will ensure an AP reset stack address does not get
set outside of the AP reset stack memory allocation.

Cc: Eric Dong <eric.dong@...>
Cc: Ray Ni <ray.ni@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Rahul Kumar <rahul1.kumar@...>
Acked-by: Ray Ni <ray.ni@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
UefiCpuPkg/Library/MpInitLib/MpLib.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index 1f47ff3f73b5..681fa79b4cff 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -680,11 +680,16 @@ MpInitLibSevEsAPReset (
IN CPU_MP_DATA *CpuMpData
)
{
+ EFI_STATUS Status;
+ UINTN ProcessorNumber;
UINT16 Code16, Code32;
AP_RESET *APResetFn;
UINTN BufferStart;
UINTN StackStart;

+ Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);
+ ASSERT_EFI_ERROR (Status);
+
Code16 = GetProtectedMode16CS ();
Code32 = GetProtectedMode32CS ();

@@ -696,7 +701,7 @@ MpInitLibSevEsAPReset (

BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
StackStart = CpuMpData->SevEsAPResetStackStart -
- (AP_RESET_STACK_SIZE * GetApicId ());
+ (AP_RESET_STACK_SIZE * ProcessorNumber);

//
// This call never returns.
--
2.28.0


[PATCH v3 10/11] UefiCpuPkg, OvmfPkg: Disable interrupts when using the GHCB

Lendacky, Thomas
 

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

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

The QemuFlashPtrWrite() flash services runtime uses the GHCB and VmgExit()
directly to perform the flash write when running as an SEV-ES guest. If an
interrupt arrives between VmgInit() and VmgExit(), the Dr7 read in the
interrupt handler will generate a #VC, which can overwrite information in
the GHCB that QemuFlashPtrWrite() has set. This has been seen with the
timer interrupt firing and the CpuExceptionHandlerLib library code,
UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/
Xcode5ExceptionHandlerAsm.nasm and
ExceptionHandlerAsm.nasm
reading the Dr7 register while QemuFlashPtrWrite() is using the GHCB. In
general, it is necessary to protect the GHCB whenever it is used, not just
in QemuFlashPtrWrite().

Disable interrupts around the usage of the GHCB by modifying the VmgInit()
and VmgDone() interfaces:
- VmgInit() will take an extra parameter that is a pointer to a BOOLEAN
that will hold the interrupt state at the time of invocation. VmgInit()
will get and save this interrupt state before updating the GHCB.
- VmgDone() will take an extra parameter that is used to indicate whether
interrupts are to be (re)enabled. Before exiting, VmgDone() will enable
interrupts if that is requested.

Fixes: 437eb3f7a8db7681afe0e6064d3a8edb12abb766
Cc: Eric Dong <eric.dong@...>
Cc: Ray Ni <ray.ni@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Rahul Kumar <rahul1.kumar@...>
Cc: Jordan Justen <jordan.l.justen@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Cc: Tom Lendacky <thomas.lendacky@...>
Cc: Brijesh Singh <brijesh.singh@...>
Reviewed-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
UefiCpuPkg/Include/Library/VmgExitLib.h | 14 ++++++++---
OvmfPkg/Library/VmgExitLib/VmgExitLib.c | 26 +++++++++++++++++---
OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c | 5 ++--
OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c | 5 ++--
UefiCpuPkg/Library/MpInitLib/DxeMpLib.c | 5 ++--
UefiCpuPkg/Library/MpInitLib/MpLib.c | 7 +++---
UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c | 18 ++++++++------
7 files changed, 55 insertions(+), 25 deletions(-)

diff --git a/UefiCpuPkg/Include/Library/VmgExitLib.h b/UefiCpuPkg/Include/Library/VmgExitLib.h
index 07e8af6450b9..061948cf840d 100644
--- a/UefiCpuPkg/Include/Library/VmgExitLib.h
+++ b/UefiCpuPkg/Include/Library/VmgExitLib.h
@@ -50,13 +50,16 @@ VmgExit (
Performs the necessary steps in preparation for invoking VMGEXIT. Must be
called before setting any fields within the GHCB.

- @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] InterruptState A pointer to hold the current interrupt
+ state, used for restoring in VmgDone ()

**/
VOID
EFIAPI
VmgInit (
- IN OUT GHCB *Ghcb
+ IN OUT GHCB *Ghcb,
+ IN OUT BOOLEAN *InterruptState
);

/**
@@ -65,13 +68,16 @@ VmgInit (
Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
called after obtaining needed fields within the GHCB.

- @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] Ghcb A pointer to the GHCB
+ @param[in] InterruptState An indicator to conditionally (re)enable
+ interrupts

**/
VOID
EFIAPI
VmgDone (
- IN OUT GHCB *Ghcb
+ IN OUT GHCB *Ghcb,
+ IN BOOLEAN InterruptState
);

/**
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.c b/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
index 0540df8a04d4..bc5cd61d751f 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.c
@@ -132,15 +132,27 @@ VmgExit (
Performs the necessary steps in preparation for invoking VMGEXIT. Must be
called before setting any fields within the GHCB.

- @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] InterruptState A pointer to hold the current interrupt
+ state, used for restoring in VmgDone ()

**/
VOID
EFIAPI
VmgInit (
- IN OUT GHCB *Ghcb
+ IN OUT GHCB *Ghcb,
+ IN OUT BOOLEAN *InterruptState
)
{
+ //
+ // Be sure that an interrupt can't cause a #VC while the GHCB is
+ // being used.
+ //
+ *InterruptState = GetInterruptState ();
+ if (*InterruptState) {
+ DisableInterrupts ();
+ }
+
SetMem (&Ghcb->SaveArea, sizeof (Ghcb->SaveArea), 0);
}

@@ -150,15 +162,21 @@ VmgInit (
Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
called after obtaining needed fields within the GHCB.

- @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] Ghcb A pointer to the GHCB
+ @param[in] InterruptState An indicator to conditionally (re)enable
+ interrupts

**/
VOID
EFIAPI
VmgDone (
- IN OUT GHCB *Ghcb
+ IN OUT GHCB *Ghcb,
+ IN BOOLEAN InterruptState
)
{
+ if (InterruptState) {
+ EnableInterrupts ();
+ }
}

/**
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
index 9bf9d160179c..1671db3a01b1 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
@@ -1568,6 +1568,7 @@ VmgExitHandleVc (
SEV_ES_INSTRUCTION_DATA InstructionData;
UINT64 ExitCode, Status;
EFI_STATUS VcRet;
+ BOOLEAN InterruptState;

VcRet = EFI_SUCCESS;

@@ -1578,7 +1579,7 @@ VmgExitHandleVc (
Regs = SystemContext.SystemContextX64;
Ghcb = Msr.Ghcb;

- VmgInit (Ghcb);
+ VmgInit (Ghcb, &InterruptState);

ExitCode = Regs->ExceptionData;
switch (ExitCode) {
@@ -1662,7 +1663,7 @@ VmgExitHandleVc (
VcRet = EFI_PROTOCOL_ERROR;
}

- VmgDone (Ghcb);
+ VmgDone (Ghcb, InterruptState);

return VcRet;
}
diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
index f9b21b54137d..1b0742967f71 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
@@ -52,6 +52,7 @@ QemuFlashPtrWrite (
if (MemEncryptSevEsIsEnabled ()) {
MSR_SEV_ES_GHCB_REGISTER Msr;
GHCB *Ghcb;
+ BOOLEAN InterruptState;

Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
Ghcb = Msr.Ghcb;
@@ -63,12 +64,12 @@ QemuFlashPtrWrite (
// #VC exception. Instead, use the the VMGEXIT MMIO write support directly
// to perform the update.
//
- VmgInit (Ghcb);
+ VmgInit (Ghcb, &InterruptState);
Ghcb->SharedBuffer[0] = Value;
Ghcb->SaveArea.SwScratch = (UINT64) (UINTN) Ghcb->SharedBuffer;
VmgSetOffsetValid (Ghcb, GhcbSwScratch);
VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, (UINT64) (UINTN) Ptr, 1);
- VmgDone (Ghcb);
+ VmgDone (Ghcb, InterruptState);
} else {
*Ptr = Value;
}
diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
index 2c00d72ddefe..7839c249760e 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c
@@ -171,6 +171,7 @@ GetSevEsAPMemory (
EFI_PHYSICAL_ADDRESS StartAddress;
MSR_SEV_ES_GHCB_REGISTER Msr;
GHCB *Ghcb;
+ BOOLEAN InterruptState;

//
// Allocate 1 page for AP jump table page
@@ -192,9 +193,9 @@ GetSevEsAPMemory (
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
Ghcb = Msr.Ghcb;

- VmgInit (Ghcb);
+ VmgInit (Ghcb, &InterruptState);
VmgExit (Ghcb, SVM_EXIT_AP_JUMP_TABLE, 0, (UINT64) (UINTN) StartAddress);
- VmgDone (Ghcb);
+ VmgDone (Ghcb, InterruptState);

return (UINTN) StartAddress;
}
diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c
index 6d977d45bcdd..1f47ff3f73b5 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.c
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c
@@ -884,6 +884,7 @@ ApWakeupFunction (
GHCB *Ghcb;
UINT64 Status;
BOOLEAN DoDecrement;
+ BOOLEAN InterruptState;

DoDecrement = (BOOLEAN) (CpuMpData->InitFlag == ApInitConfig);

@@ -891,7 +892,7 @@ ApWakeupFunction (
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
Ghcb = Msr.Ghcb;

- VmgInit (Ghcb);
+ VmgInit (Ghcb, &InterruptState);

if (DoDecrement) {
DoDecrement = FALSE;
@@ -905,11 +906,11 @@ ApWakeupFunction (

Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
- VmgDone (Ghcb);
+ VmgDone (Ghcb, InterruptState);
break;
}

- VmgDone (Ghcb);
+ VmgDone (Ghcb, InterruptState);
}

//
diff --git a/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c
index b47e282aff82..89b065cb3ff3 100644
--- a/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c
+++ b/UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.c
@@ -57,15 +57,16 @@ VmgExit (
Performs the necessary steps in preparation for invoking VMGEXIT. Must be
called before setting any fields within the GHCB.

- The base library function does nothing.
-
- @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] InterruptState A pointer to hold the current interrupt
+ state, used for restoring in VmgDone ()

**/
VOID
EFIAPI
VmgInit (
- IN OUT GHCB *Ghcb
+ IN OUT GHCB *Ghcb,
+ IN OUT BOOLEAN *InterruptState
)
{
}
@@ -76,15 +77,16 @@ VmgInit (
Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
called after obtaining needed fields within the GHCB.

- The base library function does nothing.
-
- @param[in, out] Ghcb A pointer to the GHCB
+ @param[in, out] Ghcb A pointer to the GHCB
+ @param[in] InterruptState An indicator to conditionally (re)enable
+ interrupts

**/
VOID
EFIAPI
VmgDone (
- IN OUT GHCB *Ghcb
+ IN OUT GHCB *Ghcb,
+ IN BOOLEAN InterruptState
)
{
}
--
2.28.0


[PATCH v3 09/11] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Fix erase blocks for SEV-ES

Lendacky, Thomas
 

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

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

The original SEV-ES support missed updating the QemuFlashEraseBlock()
function to successfully erase blocks. Update QemuFlashEraseBlock() to
call the QemuFlashPtrWrite() to be able to successfully perform the
commands under SEV-ES.

Fixes: 437eb3f7a8db7681afe0e6064d3a8edb12abb766
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/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
index 0d29bf701aca..d19997032ec9 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
@@ -232,8 +232,8 @@ QemuFlashEraseBlock (
}

Ptr = QemuFlashPtr (Lba, 0);
- *Ptr = BLOCK_ERASE_CMD;
- *Ptr = BLOCK_ERASE_CONFIRM_CMD;
+ QemuFlashPtrWrite (Ptr, BLOCK_ERASE_CMD);
+ QemuFlashPtrWrite (Ptr, BLOCK_ERASE_CONFIRM_CMD);
return EFI_SUCCESS;
}

--
2.28.0


[PATCH v3 08/11] OvmfPkg/QemuFlashFvbServicesRuntimeDxe: Set the SwScratch valid bit

Lendacky, Thomas
 

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

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

All fields that are set in the GHCB should have their associated bit in
the GHCB ValidBitmap field set. Add support to set the bit for the scratch
area field (SwScratch).

Fixes: 437eb3f7a8db7681afe0e6064d3a8edb12abb766
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/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
index 565383ee26d2..f9b21b54137d 100644
--- a/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
+++ b/OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
@@ -66,6 +66,7 @@ QemuFlashPtrWrite (
VmgInit (Ghcb);
Ghcb->SharedBuffer[0] = Value;
Ghcb->SaveArea.SwScratch = (UINT64) (UINTN) Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, (UINT64) (UINTN) Ptr, 1);
VmgDone (Ghcb);
} else {
--
2.28.0


[PATCH v3 07/11] UefiCpuPkg/MpInitLib: Set the SW exit fields when performing VMGEXIT

Lendacky, Thomas
 

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

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

All fields that are set in the GHCB should have their associated bit in
the GHCB ValidBitmap field set. Add support to set the bits for the
software exit information fields when performing a VMGEXIT (SwExitCode,
SwExitInfo1, SwExitInfo2).

Fixes: 20da7ca42a33d3ef767ce4129f11496af7f67c9f
Cc: Eric Dong <eric.dong@...>
Cc: Ray Ni <ray.ni@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Rahul Kumar <rahul1.kumar@...>
Acked-by: Ray Ni <ray.ni@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
index 5d30f35b201c..5532a1d391bc 100644
--- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
+++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
@@ -533,6 +533,12 @@ BITS 64

mov rax, 0x80000004 ; VMGEXIT AP_RESET_HOLD
mov [rdx + 0x390], rax
+ mov rax, 114 ; Set SwExitCode valid bit
+ bts [rdx + 0x3f0], rax
+ inc rax ; Set SwExitInfo1 valid bit
+ bts [rdx + 0x3f0], rax
+ inc rax ; Set SwExitInfo2 valid bit
+ bts [rdx + 0x3f0], rax

pop rdx
pop rcx
--
2.28.0


[PATCH v3 06/11] OvmfPkg/VmgExitLib: Set the SwScratch valid bit for MMIO events

Lendacky, Thomas
 

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

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

All fields that are set in the GHCB should have their associated bit in
the GHCB ValidBitmap field set. Add support to set the bit for the scratch
area field (SwScratch).

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

diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
index e5f14035b06f..9bf9d160179c 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
@@ -664,6 +664,7 @@ MmioExit (
CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);

Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
if (Status != 0) {
return Status;
@@ -693,6 +694,7 @@ MmioExit (
CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);

Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
if (Status != 0) {
return Status;
@@ -725,6 +727,7 @@ MmioExit (
ExitInfo2 = Bytes;

Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
if (Status != 0) {
return Status;
@@ -755,6 +758,7 @@ MmioExit (
ExitInfo2 = Bytes;

Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
if (Status != 0) {
return Status;
@@ -780,6 +784,7 @@ MmioExit (
ExitInfo2 = Bytes;

Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
if (Status != 0) {
return Status;
--
2.28.0