mirror of
https://git.proxmox.com/git/efi-boot-shim
synced 2025-08-02 15:38:52 +00:00
Add the optional HTTPBoot support
This commit adds the basic support for HTTPBoot, i.e. to fetch the next stage loader with the HTTP protocol. It requires gnu-efi >= 3.0.3 to support the URI device path and Ip4Config2 or Ip6Config protocol support in the UEFI implementation. To build shim.efi with HTTPBoot support: make ENABLE_HTTPBOOT=1 shim.efi Signed-off-by: Gary Ching-Pang Lin <glin@suse.com>
This commit is contained in:
parent
8438ae7a01
commit
3d79bcb265
9
Makefile
9
Makefile
@ -38,6 +38,10 @@ ifneq ($(origin OVERRIDE_SECURITY_POLICY), undefined)
|
||||
CFLAGS += -DOVERRIDE_SECURITY_POLICY
|
||||
endif
|
||||
|
||||
ifneq ($(origin ENABLE_HTTPBOOT), undefined)
|
||||
CFLAGS += -DENABLE_HTTPBOOT
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),x86_64)
|
||||
CFLAGS += -mno-mmx -mno-sse -mno-red-zone -nostdinc \
|
||||
-maccumulate-outgoing-args \
|
||||
@ -91,6 +95,11 @@ MOK_SOURCES = MokManager.c shim.h include/console.h PasswordCrypt.c PasswordCryp
|
||||
FALLBACK_OBJS = fallback.o
|
||||
FALLBACK_SRCS = fallback.c
|
||||
|
||||
ifneq ($(origin ENABLE_HTTPBOOT), undefined)
|
||||
OBJS += httpboot.o
|
||||
SOURCES += httpboot.c httpboot.h
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
shim.crt:
|
||||
|
774
httpboot.c
Normal file
774
httpboot.c
Normal file
@ -0,0 +1,774 @@
|
||||
/*
|
||||
* Copyright 2015 SUSE LINUX GmbH <glin@suse.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Significant portions of this code are derived from Tianocore
|
||||
* (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
|
||||
* Corporation.
|
||||
*/
|
||||
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#include "str.h"
|
||||
#include "Http.h"
|
||||
#include "Ip4Config2.h"
|
||||
#include "Ip6Config.h"
|
||||
|
||||
extern UINT8 in_protocol;
|
||||
|
||||
#define perror(fmt, ...) ({ \
|
||||
UINTN __perror_ret = 0; \
|
||||
if (!in_protocol) \
|
||||
__perror_ret = Print((fmt), ##__VA_ARGS__); \
|
||||
__perror_ret; \
|
||||
})
|
||||
|
||||
static UINTN
|
||||
ascii_to_int (CONST CHAR8 *str)
|
||||
{
|
||||
UINTN u;
|
||||
CHAR8 c;
|
||||
|
||||
// skip preceeding white space
|
||||
while (*str && *str == ' ') {
|
||||
str += 1;
|
||||
}
|
||||
|
||||
// convert digits
|
||||
u = 0;
|
||||
while ((c = *(str++))) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
u = (u * 10) + c - '0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
static UINTN
|
||||
convert_http_status_code (EFI_HTTP_STATUS_CODE status_code)
|
||||
{
|
||||
if (status_code >= HTTP_STATUS_100_CONTINUE &&
|
||||
status_code < HTTP_STATUS_200_OK) {
|
||||
return (status_code - HTTP_STATUS_100_CONTINUE + 100);
|
||||
} else if (status_code >= HTTP_STATUS_200_OK &&
|
||||
status_code < HTTP_STATUS_300_MULTIPLE_CHIOCES) {
|
||||
return (status_code - HTTP_STATUS_200_OK + 200);
|
||||
} else if (status_code >= HTTP_STATUS_300_MULTIPLE_CHIOCES &&
|
||||
status_code < HTTP_STATUS_400_BAD_REQUEST) {
|
||||
return (status_code - HTTP_STATUS_300_MULTIPLE_CHIOCES + 300);
|
||||
} else if (status_code >= HTTP_STATUS_400_BAD_REQUEST &&
|
||||
status_code < HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
|
||||
return (status_code - HTTP_STATUS_400_BAD_REQUEST + 400);
|
||||
} else if (status_code >= HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
|
||||
return (status_code - HTTP_STATUS_500_INTERNAL_SERVER_ERROR + 500);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static EFI_DEVICE_PATH *devpath;
|
||||
static EFI_MAC_ADDRESS mac_addr;
|
||||
static IPv4_DEVICE_PATH ip4_node;
|
||||
static IPv6_DEVICE_PATH ip6_node;
|
||||
static BOOLEAN is_ip6;
|
||||
static CHAR8 *uri;
|
||||
|
||||
BOOLEAN
|
||||
find_httpboot (EFI_HANDLE device)
|
||||
{
|
||||
EFI_DEVICE_PATH *unpacked;
|
||||
EFI_DEVICE_PATH *Node;
|
||||
EFI_DEVICE_PATH *NextNode;
|
||||
MAC_ADDR_DEVICE_PATH *MacNode;
|
||||
URI_DEVICE_PATH *UriNode;
|
||||
UINTN uri_size;
|
||||
|
||||
if (!uri)
|
||||
FreePool(uri);
|
||||
|
||||
devpath = DevicePathFromHandle(device);
|
||||
if (!devpath) {
|
||||
perror(L"Failed to get device path\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
unpacked = UnpackDevicePath(devpath);
|
||||
if (!unpacked) {
|
||||
perror(L"Failed to unpack device path\n");
|
||||
return FALSE;
|
||||
}
|
||||
Node = unpacked;
|
||||
|
||||
/* Traverse the device path to find IPv4()/Uri() or IPv6()/Uri() */
|
||||
while (!IsDevicePathEnd(Node)) {
|
||||
/* Save the MAC node so we can match the net card later */
|
||||
if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
|
||||
DevicePathSubType(Node) == MSG_MAC_ADDR_DP) {
|
||||
MacNode = (MAC_ADDR_DEVICE_PATH *)Node;
|
||||
CopyMem(&mac_addr, &MacNode->MacAddress, sizeof(EFI_MAC_ADDRESS));
|
||||
}
|
||||
|
||||
if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
|
||||
(DevicePathSubType(Node) == MSG_IPv4_DP ||
|
||||
DevicePathSubType(Node) == MSG_IPv6_DP)) {
|
||||
/* Save the IP node so we can set up the connection later */
|
||||
if (DevicePathSubType(Node) == MSG_IPv6_DP) {
|
||||
CopyMem(&ip6_node, Node, sizeof(IPv6_DEVICE_PATH));
|
||||
is_ip6 = TRUE;
|
||||
} else {
|
||||
CopyMem(&ip4_node, Node, sizeof(IPv4_DEVICE_PATH));
|
||||
is_ip6 = FALSE;
|
||||
}
|
||||
|
||||
Node = NextDevicePathNode(Node);
|
||||
NextNode = NextDevicePathNode(Node);
|
||||
if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
|
||||
DevicePathSubType(Node) == MSG_URI_DP &&
|
||||
IsDevicePathEnd(NextNode)) {
|
||||
/* Save the current URI */
|
||||
UriNode = (URI_DEVICE_PATH *)Node;
|
||||
uri_size = strlena(UriNode->Uri);
|
||||
uri = AllocatePool(uri_size + 1);
|
||||
if (!uri) {
|
||||
perror(L"Failed to allocate uri\n");
|
||||
return FALSE;
|
||||
}
|
||||
CopyMem(uri, UriNode->Uri, uri_size + 1);
|
||||
FreePool(unpacked);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
Node = NextDevicePathNode(Node);
|
||||
}
|
||||
|
||||
FreePool(unpacked);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
generate_next_uri (CONST CHAR8 *current_uri, CONST CHAR8 *next_loader,
|
||||
CHAR8 **uri)
|
||||
{
|
||||
CONST CHAR8 *ptr;
|
||||
UINTN next_len;
|
||||
UINTN path_len = 0;
|
||||
UINTN count = 0;
|
||||
|
||||
if (strncmpa(current_uri, (CHAR8 *)"http://", 7) == 0) {
|
||||
ptr = current_uri + 7;
|
||||
count += 7;
|
||||
} else {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Extract the path */
|
||||
next_len = strlena(next_loader);
|
||||
while (*ptr != '\0') {
|
||||
count++;
|
||||
if (*ptr == '/')
|
||||
path_len = count;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
*uri = AllocatePool(sizeof(CHAR8) * (path_len + next_len + 1));
|
||||
if (!*uri)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
CopyMem(*uri, current_uri, path_len);
|
||||
CopyMem(*uri + path_len, next_loader, next_len);
|
||||
(*uri)[path_len + next_len] = '\0';
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
extract_hostname (CONST CHAR8 *url, CHAR8 **hostname)
|
||||
{
|
||||
CONST CHAR8 *ptr, *start;
|
||||
UINTN host_len = 0;
|
||||
|
||||
if (strncmpa(url, (CHAR8 *)"http://", 7) == 0)
|
||||
start = url + 7;
|
||||
else
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
ptr = start;
|
||||
while (*ptr != '/' && *ptr != '\0') {
|
||||
host_len++;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
*hostname = AllocatePool(sizeof(CHAR8) * (host_len + 1));
|
||||
if (!*hostname)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
CopyMem(*hostname, start, host_len);
|
||||
(*hostname)[host_len] = '\0';
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
#define SAME_MAC_ADDR(a, b) (!CompareMem(a, b, sizeof(EFI_MAC_ADDRESS)))
|
||||
|
||||
static EFI_HANDLE
|
||||
get_nic_handle (EFI_MAC_ADDRESS *mac)
|
||||
{
|
||||
EFI_GUID http_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
|
||||
EFI_DEVICE_PATH *unpacked = NULL;
|
||||
EFI_DEVICE_PATH *Node;
|
||||
EFI_DEVICE_PATH *temp_path = NULL;
|
||||
MAC_ADDR_DEVICE_PATH *MacNode;
|
||||
EFI_HANDLE handle = NULL;
|
||||
EFI_HANDLE *buffer;
|
||||
UINTN NoHandles;
|
||||
UINTN i;
|
||||
EFI_STATUS status;
|
||||
|
||||
/* Get the list of handles that support the HTTP service binding
|
||||
protocol */
|
||||
status = uefi_call_wrapper(BS->LocateHandleBuffer, 5,
|
||||
ByProtocol,
|
||||
&http_binding_guid,
|
||||
NULL,
|
||||
&NoHandles,
|
||||
&buffer);
|
||||
if (EFI_ERROR(status))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < NoHandles; i++) {
|
||||
temp_path = DevicePathFromHandle(buffer[i]);
|
||||
|
||||
/* Match the MAC address */
|
||||
unpacked = UnpackDevicePath(temp_path);
|
||||
if (!unpacked) {
|
||||
perror(L"Failed to unpack device path\n");
|
||||
continue;
|
||||
}
|
||||
Node = unpacked;
|
||||
while (!IsDevicePathEnd(Node)) {
|
||||
if (DevicePathType(Node) == MESSAGING_DEVICE_PATH &&
|
||||
DevicePathSubType(Node) == MSG_MAC_ADDR_DP) {
|
||||
MacNode = (MAC_ADDR_DEVICE_PATH *)Node;
|
||||
if (SAME_MAC_ADDR(mac, &MacNode->MacAddress)) {
|
||||
handle = buffer[i];
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
Node = NextDevicePathNode(Node);
|
||||
}
|
||||
FreePool(unpacked);
|
||||
unpacked = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
if (unpacked)
|
||||
FreePool(unpacked);
|
||||
FreePool(buffer);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static BOOLEAN
|
||||
is_unspecified_addr (EFI_IPv6_ADDRESS ip6)
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
for (i = 0; i<16; i++) {
|
||||
if (ip6.Addr[i] != 0)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
set_ip6(EFI_HANDLE *nic, IPv6_DEVICE_PATH *ip6node)
|
||||
{
|
||||
EFI_GUID ip6_config_guid = EFI_IP6_CONFIG_PROTOCOL_GUID;
|
||||
EFI_IP6_CONFIG_PROTOCOL *ip6cfg;
|
||||
EFI_IP6_CONFIG_MANUAL_ADDRESS ip6;
|
||||
EFI_IPv6_ADDRESS gateway;
|
||||
EFI_STATUS status;
|
||||
|
||||
status = uefi_call_wrapper(BS->HandleProtocol, 3,
|
||||
nic,
|
||||
&ip6_config_guid,
|
||||
(VOID **)&ip6cfg);
|
||||
if (EFI_ERROR (status))
|
||||
return status;
|
||||
|
||||
ip6.Address = ip6node->LocalIpAddress;
|
||||
ip6.PrefixLength = ip6node->PrefixLength;
|
||||
ip6.IsAnycast = FALSE;
|
||||
status = uefi_call_wrapper(ip6cfg->SetData, 4,
|
||||
ip6cfg,
|
||||
Ip6ConfigDataTypeManualAddress,
|
||||
sizeof(ip6),
|
||||
&ip6);
|
||||
if (EFI_ERROR (status))
|
||||
return status;
|
||||
|
||||
gateway = ip6node->GatewayIpAddress;
|
||||
if (is_unspecified_addr(gateway))
|
||||
return EFI_SUCCESS;
|
||||
|
||||
status = uefi_call_wrapper(ip6cfg->SetData, 4,
|
||||
ip6cfg,
|
||||
Ip6ConfigDataTypeGateway,
|
||||
sizeof(gateway),
|
||||
&gateway);
|
||||
if (EFI_ERROR (status))
|
||||
return status;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
set_ip4(EFI_HANDLE *nic, IPv4_DEVICE_PATH *ip4node)
|
||||
{
|
||||
EFI_GUID ip4_config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID;
|
||||
EFI_IP4_CONFIG2_PROTOCOL *ip4cfg2;
|
||||
EFI_IP4_CONFIG2_MANUAL_ADDRESS ip4;
|
||||
EFI_IPv4_ADDRESS gateway;
|
||||
EFI_STATUS status;
|
||||
|
||||
status = uefi_call_wrapper(BS->HandleProtocol, 3,
|
||||
nic,
|
||||
&ip4_config2_guid,
|
||||
(VOID **)&ip4cfg2);
|
||||
if (EFI_ERROR (status))
|
||||
return status;
|
||||
|
||||
ip4.Address = ip4node->LocalIpAddress;
|
||||
ip4.SubnetMask = ip4node->SubnetMask;
|
||||
status = uefi_call_wrapper(ip4cfg2->SetData, 4,
|
||||
ip4cfg2,
|
||||
Ip4Config2DataTypeManualAddress,
|
||||
sizeof(ip4),
|
||||
&ip4);
|
||||
if (EFI_ERROR (status))
|
||||
return status;
|
||||
|
||||
gateway = ip4node->GatewayIpAddress;
|
||||
status = uefi_call_wrapper(ip4cfg2->SetData, 4,
|
||||
ip4cfg2,
|
||||
Ip4Config2DataTypeGateway,
|
||||
sizeof(gateway),
|
||||
&gateway);
|
||||
if (EFI_ERROR (status))
|
||||
return status;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID EFIAPI
|
||||
httpnotify (EFI_EVENT Event, VOID *Context)
|
||||
{
|
||||
*((BOOLEAN *) Context) = TRUE;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
configure_http (EFI_HTTP_PROTOCOL *http, BOOLEAN is_ip6)
|
||||
{
|
||||
EFI_HTTP_CONFIG_DATA http_mode;
|
||||
EFI_HTTPv4_ACCESS_POINT ip4node;
|
||||
EFI_HTTPv6_ACCESS_POINT ip6node;
|
||||
|
||||
/* Configure HTTP */
|
||||
ZeroMem(&http_mode, sizeof(http_mode));
|
||||
http_mode.HttpVersion = HttpVersion11;
|
||||
/* use the default time out */
|
||||
http_mode.TimeOutMillisec = 0;
|
||||
|
||||
if (!is_ip6) {
|
||||
http_mode.LocalAddressIsIPv6 = FALSE;
|
||||
ZeroMem(&ip4node, sizeof(ip4node));
|
||||
ip4node.UseDefaultAddress = TRUE;
|
||||
http_mode.AccessPoint.IPv4Node = &ip4node;
|
||||
} else {
|
||||
http_mode.LocalAddressIsIPv6 = TRUE;
|
||||
ZeroMem(&ip6node, sizeof(ip6node));
|
||||
http_mode.AccessPoint.IPv6Node = &ip6node;
|
||||
}
|
||||
|
||||
return uefi_call_wrapper(http->Configure, 2, http, &http_mode);
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
send_http_request (EFI_HTTP_PROTOCOL *http, CHAR8 *hostname, CHAR8 *uri)
|
||||
{
|
||||
EFI_HTTP_TOKEN tx_token;
|
||||
EFI_HTTP_MESSAGE tx_message;
|
||||
EFI_HTTP_REQUEST_DATA request;
|
||||
EFI_HTTP_HEADER headers[3];
|
||||
BOOLEAN request_done;
|
||||
CHAR16 *Url = NULL;
|
||||
EFI_STATUS status;
|
||||
EFI_STATUS event_status;
|
||||
|
||||
/* Convert the ascii string to the UCS2 string */
|
||||
Url = PoolPrint(L"%a", uri);
|
||||
if (!Url)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
request.Method = HttpMethodGet;
|
||||
request.Url = Url;
|
||||
|
||||
/* Prepare the HTTP headers */
|
||||
headers[0].FieldName = (CHAR8 *)"Host";
|
||||
headers[0].FieldValue = hostname;
|
||||
headers[1].FieldName = (CHAR8 *)"Accept";
|
||||
headers[1].FieldValue = (CHAR8 *)"*/*";
|
||||
headers[2].FieldName = (CHAR8 *)"User-Agent";
|
||||
headers[2].FieldValue = (CHAR8 *)"UefiHttpBoot/1.0";
|
||||
|
||||
tx_message.Data.Request = &request;
|
||||
tx_message.HeaderCount = 3;
|
||||
tx_message.Headers = headers;
|
||||
tx_message.BodyLength = 0;
|
||||
tx_message.Body = NULL;
|
||||
|
||||
tx_token.Status = EFI_NOT_READY;
|
||||
tx_token.Message = &tx_message;
|
||||
tx_token.Event = NULL;
|
||||
request_done = FALSE;
|
||||
status = uefi_call_wrapper(BS->CreateEvent, 5,
|
||||
EVT_NOTIFY_SIGNAL,
|
||||
TPL_NOTIFY,
|
||||
httpnotify,
|
||||
&request_done,
|
||||
&tx_token.Event);
|
||||
if (EFI_ERROR(status)) {
|
||||
perror(L"Failed to Create Event for HTTP request: %r\n", status);
|
||||
goto no_event;
|
||||
}
|
||||
|
||||
/* Send out the request */
|
||||
status = uefi_call_wrapper(http->Request, 2, http, &tx_token);
|
||||
if (EFI_ERROR(status)) {
|
||||
perror(L"HTTP request failed: %r\n", status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Wait for the response */
|
||||
while (!request_done)
|
||||
uefi_call_wrapper(http->Poll, 1, http);
|
||||
|
||||
if (EFI_ERROR(tx_token.Status)) {
|
||||
perror(L"HTTP request: %r\n", tx_token.Status);
|
||||
status = tx_token.Status;
|
||||
}
|
||||
|
||||
error:
|
||||
event_status = uefi_call_wrapper(BS->CloseEvent, 1, tx_token.Event);
|
||||
if (EFI_ERROR(event_status)) {
|
||||
perror(L"Failed to close Event for HTTP request: %r\n",
|
||||
event_status);
|
||||
}
|
||||
|
||||
no_event:
|
||||
if (Url)
|
||||
FreePool(Url);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINTN *buf_size)
|
||||
{
|
||||
EFI_HTTP_TOKEN rx_token;
|
||||
EFI_HTTP_MESSAGE rx_message;
|
||||
EFI_HTTP_RESPONSE_DATA response;
|
||||
EFI_HTTP_STATUS_CODE http_status;
|
||||
BOOLEAN response_done;
|
||||
UINTN i, downloaded;
|
||||
CHAR8 rx_buffer[9216];
|
||||
EFI_STATUS status;
|
||||
EFI_STATUS event_status;
|
||||
|
||||
/* Initialize the rx message and buffer */
|
||||
response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS;
|
||||
rx_message.Data.Response = &response;
|
||||
rx_message.HeaderCount = 0;
|
||||
rx_message.Headers = 0;
|
||||
rx_message.BodyLength = sizeof(rx_buffer);
|
||||
rx_message.Body = rx_buffer;
|
||||
|
||||
rx_token.Status = EFI_NOT_READY;
|
||||
rx_token.Message = &rx_message;
|
||||
rx_token.Event = NULL;
|
||||
response_done = FALSE;
|
||||
status = uefi_call_wrapper(BS->CreateEvent, 5,
|
||||
EVT_NOTIFY_SIGNAL,
|
||||
TPL_NOTIFY,
|
||||
httpnotify,
|
||||
&response_done,
|
||||
&rx_token.Event);
|
||||
if (EFI_ERROR(status)) {
|
||||
perror(L"Failed to Create Event for HTTP response: %r\n", status);
|
||||
goto no_event;
|
||||
}
|
||||
|
||||
/* Notify the firmware to receive the HTTP messages */
|
||||
status = uefi_call_wrapper(http->Response, 2, http, &rx_token);
|
||||
if (EFI_ERROR(status)) {
|
||||
perror(L"HTTP response failed: %r\n", status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Wait for the response */
|
||||
while (!response_done)
|
||||
uefi_call_wrapper(http->Poll, 1, http);
|
||||
|
||||
if (EFI_ERROR(rx_token.Status)) {
|
||||
perror(L"HTTP response: %r\n", rx_token.Status);
|
||||
status = rx_token.Status;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Check the HTTP status code */
|
||||
http_status = rx_token.Message->Data.Response->StatusCode;
|
||||
if (http_status != HTTP_STATUS_200_OK) {
|
||||
perror(L"HTTP Status Code: %d\n",
|
||||
convert_http_status_code(http_status));
|
||||
status = EFI_ABORTED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Check the length of the file */
|
||||
for (i = 0; i < rx_message.HeaderCount; i++) {
|
||||
if (!strcmpa(rx_message.Headers[i].FieldName, (CHAR8 *)"Content-Length")) {
|
||||
*buf_size = ascii_to_int(rx_message.Headers[i].FieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (*buf_size == 0) {
|
||||
perror(L"Failed to get Content-Lenght\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
*buffer = AllocatePool(*buf_size);
|
||||
if (!*buffer) {
|
||||
perror(L"Failed to allocate new rx buffer\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
downloaded = rx_message.BodyLength;
|
||||
|
||||
CopyMem(*buffer, rx_buffer, downloaded);
|
||||
|
||||
/* Retreive the rest of the message */
|
||||
while (downloaded < *buf_size) {
|
||||
if (rx_message.Headers) {
|
||||
FreePool(rx_message.Headers);
|
||||
}
|
||||
rx_message.Headers = NULL;
|
||||
rx_message.HeaderCount = 0;
|
||||
rx_message.Data.Response = NULL;
|
||||
rx_message.BodyLength = sizeof(rx_buffer);
|
||||
rx_message.Body = rx_buffer;
|
||||
|
||||
rx_token.Status = EFI_NOT_READY;
|
||||
response_done = FALSE;
|
||||
|
||||
status = uefi_call_wrapper(http->Response, 2, http, &rx_token);
|
||||
if (EFI_ERROR(status)) {
|
||||
perror(L"HTTP response failed: %r\n", status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (!response_done)
|
||||
uefi_call_wrapper(http->Poll, 1, http);
|
||||
|
||||
if (EFI_ERROR(rx_token.Status)) {
|
||||
perror(L"HTTP response: %r\n", rx_token.Status);
|
||||
status = rx_token.Status;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (rx_message.BodyLength + downloaded > *buf_size) {
|
||||
status = EFI_BAD_BUFFER_SIZE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
CopyMem(*buffer + downloaded, rx_buffer, rx_message.BodyLength);
|
||||
|
||||
downloaded += rx_message.BodyLength;
|
||||
}
|
||||
|
||||
error:
|
||||
event_status = uefi_call_wrapper(BS->CloseEvent, 1, rx_token.Event);
|
||||
if (EFI_ERROR(event_status)) {
|
||||
perror(L"Failed to close Event for HTTP response: %r\n",
|
||||
event_status);
|
||||
}
|
||||
|
||||
no_event:
|
||||
if (EFI_ERROR(status) && *buffer)
|
||||
FreePool(*buffer);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static EFI_STATUS
|
||||
http_fetch (EFI_HANDLE image, EFI_HANDLE device,
|
||||
CHAR8 *hostname, CHAR8 *uri, BOOLEAN is_ip6,
|
||||
VOID **buffer, UINTN *buf_size)
|
||||
{
|
||||
EFI_GUID http_binding_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
|
||||
EFI_GUID http_protocol_guid = EFI_HTTP_PROTOCOL_GUID;
|
||||
EFI_SERVICE_BINDING *service;
|
||||
EFI_HANDLE http_handle;
|
||||
EFI_HTTP_PROTOCOL *http;
|
||||
EFI_STATUS status;
|
||||
EFI_STATUS child_status;
|
||||
|
||||
*buffer = NULL;
|
||||
*buf_size = 0;
|
||||
|
||||
/* Open HTTP Service Binding Protocol */
|
||||
status = uefi_call_wrapper(BS->OpenProtocol, 6, device,
|
||||
&http_binding_guid, (VOID **)&service,
|
||||
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||||
if (EFI_ERROR (status))
|
||||
return status;
|
||||
|
||||
/* Create the ChildHandle from the Service Binding */
|
||||
/* Set the handle to NULL to request a new handle */
|
||||
http_handle = NULL;
|
||||
status = uefi_call_wrapper(service->CreateChild, 2, service,
|
||||
&http_handle);
|
||||
if (EFI_ERROR (status))
|
||||
return status;
|
||||
|
||||
/* Get the http protocol */
|
||||
status = uefi_call_wrapper(BS->HandleProtocol, 3, http_handle,
|
||||
&http_protocol_guid, (VOID **)&http);
|
||||
if (EFI_ERROR (status)) {
|
||||
perror(L"Failed to get http\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = configure_http(http, is_ip6);
|
||||
if (EFI_ERROR (status)) {
|
||||
perror(L"Failed to configure http: %r\n", status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = send_http_request(http, hostname, uri);
|
||||
if (EFI_ERROR(status)) {
|
||||
perror(L"Failed to send HTTP request: %r\n", status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = receive_http_response(http, buffer, buf_size);
|
||||
if (EFI_ERROR(status)) {
|
||||
perror(L"Failed to receive HTTP response: %r\n", status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
child_status = uefi_call_wrapper(service->DestroyChild, 2, service,
|
||||
http_handle);
|
||||
|
||||
if (EFI_ERROR(status)) {
|
||||
return status;
|
||||
} else if (EFI_ERROR(child_status)) {
|
||||
return child_status;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
httpboot_fetch_buffer (EFI_HANDLE image, VOID **buffer, UINTN *buf_size)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
EFI_HANDLE nic;
|
||||
CHAR8 *next_loader = NULL;
|
||||
CHAR8 *next_uri = NULL;
|
||||
CHAR8 *hostname = NULL;
|
||||
|
||||
if (!uri)
|
||||
return EFI_NOT_READY;
|
||||
|
||||
next_loader = translate_slashes(DEFAULT_LOADER_CHAR);
|
||||
|
||||
/* Create the URI for the next loader based on the original URI */
|
||||
status = generate_next_uri(uri, next_loader, &next_uri);
|
||||
if (EFI_ERROR (status)) {
|
||||
perror(L"Next URI: %a, %r\n", next_uri, status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Extract the hostname (or IP) from URI */
|
||||
status = extract_hostname(uri, &hostname);
|
||||
if (EFI_ERROR (status)) {
|
||||
perror(L"hostname: %a, %r\n", hostname, status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Get the handle that associates with the NIC we are using and
|
||||
also supports the HTTP service binding protocol */
|
||||
nic = get_nic_handle(&mac_addr);
|
||||
if (!nic) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* UEFI stops DHCP after fetching the image and stores the related
|
||||
information in the device path node. We have to set up the
|
||||
connection on our own for the further operations. */
|
||||
if (!is_ip6)
|
||||
status = set_ip4(nic, &ip4_node);
|
||||
else
|
||||
status = set_ip6(nic, &ip6_node);
|
||||
if (EFI_ERROR (status)) {
|
||||
perror(L"Failed to set IP for HTTPBoot: %r\n", status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Use HTTP protocl to fetch the remote file */
|
||||
status = http_fetch (image, nic, hostname, next_uri, is_ip6,
|
||||
buffer, buf_size);
|
||||
if (EFI_ERROR (status)) {
|
||||
perror(L"Failed to fetch image: %r\n", status);
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
FreePool(uri);
|
||||
uri = NULL;
|
||||
if (next_uri)
|
||||
FreePool(next_uri);
|
||||
if (hostname)
|
||||
FreePool(hostname);
|
||||
|
||||
return status;
|
||||
}
|
41
httpboot.h
Normal file
41
httpboot.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2015 SUSE LINUX GmbH <glin@suse.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Significant portions of this code are derived from Tianocore
|
||||
* (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
|
||||
* Corporation.
|
||||
*/
|
||||
|
||||
#ifndef _HTTPBOOT_H_
|
||||
#define _HTTPBOOT_H_
|
||||
|
||||
BOOLEAN find_httpboot (EFI_DEVICE_PATH *devpath);
|
||||
|
||||
EFI_STATUS httpboot_fetch_buffer (EFI_HANDLE image, VOID **buffer, UINTN *buf_size);
|
||||
|
||||
#endif
|
12
shim.c
12
shim.c
@ -39,6 +39,7 @@
|
||||
#include "PeImage.h"
|
||||
#include "shim.h"
|
||||
#include "netboot.h"
|
||||
#include "httpboot.h"
|
||||
#include "shim_cert.h"
|
||||
#include "replacements.h"
|
||||
#include "tpm.h"
|
||||
@ -1722,6 +1723,17 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
|
||||
}
|
||||
data = sourcebuffer;
|
||||
datasize = sourcesize;
|
||||
#if defined(ENABLE_HTTPBOOT)
|
||||
} else if (find_httpboot(li->DeviceHandle)) {
|
||||
efi_status = httpboot_fetch_buffer (image_handle, &sourcebuffer,
|
||||
&sourcesize);
|
||||
if (efi_status != EFI_SUCCESS) {
|
||||
perror(L"Unable to fetch HTTP image: %r\n", efi_status);
|
||||
return efi_status;
|
||||
}
|
||||
data = sourcebuffer;
|
||||
datasize = sourcesize;
|
||||
#endif
|
||||
} else {
|
||||
/*
|
||||
* Read the new executable off disk
|
||||
|
Loading…
Reference in New Issue
Block a user