/*
|
# Copyright (c) 2014-2018, Linaro Limited. All rights reserved.
|
#
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
#
|
*/
|
#include <RdkBootManagerLib.h>
|
|
#define FILE_HDR_SIZE 16
|
|
STATIC EFI_LOAD_FILE_PROTOCOL *LoadFile = NULL;
|
STATIC HTTP_BOOT_PRIVATE_DATA *Private = NULL;
|
|
STATIC
|
VOID
|
HttpPrivateFromLoadFile (
|
IN EFI_LOAD_FILE_PROTOCOL *LoadFile,
|
OUT HTTP_BOOT_PRIVATE_DATA **Private
|
)
|
{
|
HTTP_BOOT_VIRTUAL_NIC *Ip4Nic = NULL;
|
|
UINTN Offset = (UINTN)&Ip4Nic->LoadFile;
|
|
Ip4Nic = (VOID *)((CHAR8 *)LoadFile - Offset);
|
ASSERT (Ip4Nic->Signature == HTTP_BOOT_VIRTUAL_NIC_SIGNATURE);
|
*Private = Ip4Nic->Private;
|
}
|
|
STATIC
|
VOID
|
HttpGetLoadFileHandle (
|
OUT EFI_LOAD_FILE_PROTOCOL **LoadFile
|
)
|
{
|
EFI_STATUS Status;
|
UINTN LoopIndex;
|
UINTN NumHandles;
|
EFI_HANDLE *AllHandles;
|
EFI_HANDLE Handle;
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText;
|
UINT16 *DeviceFullPath;
|
|
Status = gBS->LocateProtocol (
|
&gEfiDevicePathToTextProtocolGuid,
|
NULL,
|
(VOID **) &DevPathToText
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
// Get every LoadFile protocol instance installed in the system
|
Status = gBS->LocateHandleBuffer (
|
ByProtocol,
|
&gEfiLoadFileProtocolGuid,
|
NULL,
|
&NumHandles,
|
&AllHandles
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
// Get HTTP driver handle from AllHandles
|
for (LoopIndex = 0; LoopIndex < NumHandles; LoopIndex++) {
|
|
Handle = AllHandles[LoopIndex];
|
|
// Get the device path for the handle
|
Status = gBS->OpenProtocol (
|
Handle,
|
&gEfiDevicePathProtocolGuid,
|
(VOID **) &DevicePath,
|
gImageHandle,
|
NULL,
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
DeviceFullPath = DevPathToText->ConvertDevicePathToText (
|
DevicePath,
|
FALSE,
|
TRUE
|
);
|
|
ASSERT (DeviceFullPath != NULL);
|
|
if (StrStr (DeviceFullPath, L"IPv4") != NULL) {
|
DEBUG((DEBUG_INFO, "IPv4 protocol found\n"));
|
Status = gBS->OpenProtocol (
|
Handle,
|
&gEfiLoadFileProtocolGuid,
|
(VOID **) LoadFile,
|
gImageHandle,
|
NULL,
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
FreePool (AllHandles);
|
break;
|
}
|
}
|
|
ASSERT (LoopIndex < NumHandles);
|
}
|
|
STATIC
|
EFI_STATUS
|
HttpUpdatePath (
|
IN CHAR16 *Uri,
|
OUT EFI_DEVICE_PATH_PROTOCOL **NewDevicePath
|
)
|
{
|
EFI_DEV_PATH *Node;
|
EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
|
EFI_STATUS Status;
|
UINTN Index;
|
UINTN Length;
|
CHAR8 AsciiUri[URI_STR_MAX_SIZE];
|
|
Node = NULL;
|
TmpDevicePath = NULL;
|
Status = EFI_SUCCESS;
|
|
// Convert the scheme to all lower case.
|
for (Index = 0; Index < StrLen (Uri); Index++) {
|
if (Uri[Index] == L':') {
|
break;
|
}
|
if (Uri[Index] >= L'A' && Uri[Index] <= L'Z') {
|
Uri[Index] -= (CHAR16)(L'A' - L'a');
|
}
|
}
|
|
// Only accept empty URI, or http and https URI.
|
if ((StrLen (Uri) != 0) &&
|
(StrnCmp (Uri, L"http://", 7) != 0) &&
|
(StrnCmp (Uri, L"https://", 8) != 0)) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
// Create a new device path by appending the IP node and URI node to
|
// the driver's parent device path
|
Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
|
if (Node == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
goto Exit;
|
}
|
Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
|
Node->Ipv4.Header.SubType = MSG_IPv4_DP;
|
SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
|
TmpDevicePath = AppendDevicePathNode (\
|
Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*)Node);
|
FreePool (Node);
|
if (TmpDevicePath == NULL) {
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
// Update the URI node with the input boot file URI.
|
UnicodeStrToAsciiStrS (Uri, AsciiUri, sizeof (AsciiUri));
|
Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (AsciiUri);
|
Node = AllocatePool (Length);
|
if (Node == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
FreePool (TmpDevicePath);
|
goto Exit;
|
}
|
Node->DevPath.Type = MESSAGING_DEVICE_PATH;
|
Node->DevPath.SubType = MSG_URI_DP;
|
|
SetDevicePathNodeLength (Node, Length);
|
CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), \
|
AsciiUri, AsciiStrSize (AsciiUri));
|
*NewDevicePath = AppendDevicePathNode (TmpDevicePath, \
|
(EFI_DEVICE_PATH_PROTOCOL*) Node);
|
|
FreePool (Node);
|
FreePool (TmpDevicePath);
|
|
if (*NewDevicePath == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
goto Exit;
|
}
|
|
Exit:
|
|
return Status;
|
}
|
|
STATIC
|
EFI_STATUS
|
HttpGetImage (
|
IN CHAR16 *Uri,
|
OUT UINT8 **FileBuffer,
|
OUT UINTN *FileSize
|
)
|
{
|
EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
|
EFI_STATUS Status;
|
|
*FileBuffer = NULL;
|
NewDevicePath = NULL;
|
*FileSize = 0;
|
|
// Get the LoadFile Handle and
|
// Private structure of HTTP driver
|
if (LoadFile == NULL) {
|
HttpGetLoadFileHandle (&LoadFile);
|
HttpPrivateFromLoadFile (LoadFile, &Private);
|
}
|
|
// Update URI path
|
Status = HttpUpdatePath (Uri, &NewDevicePath);
|
if (EFI_ERROR (Status)) {
|
goto Exit;
|
}
|
|
// Get the HTTP image from server
|
Status = LoadFile->LoadFile (LoadFile, NewDevicePath, \
|
TRUE, FileSize, *FileBuffer);
|
if((Status != EFI_WARN_FILE_SYSTEM) && \
|
(Status != EFI_BUFFER_TOO_SMALL)) {
|
goto Exit;
|
}
|
|
*FileBuffer = AllocatePool (*FileSize);
|
if (*FileBuffer == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
goto Exit;
|
}
|
|
Status = LoadFile->LoadFile (LoadFile, NewDevicePath, \
|
TRUE, FileSize, *FileBuffer);
|
if (EFI_ERROR (Status)) {
|
FreePool (FileBuffer);
|
goto Exit;
|
}
|
|
Exit:
|
|
if (NewDevicePath != NULL) {
|
FreePool (NewDevicePath);
|
}
|
|
return Status;
|
}
|
|
UINTN
|
ParseHeader (
|
VOID * Str
|
)
|
{
|
UINTN i, Size;
|
UINT8 *Ptr;
|
|
Ptr = Str;
|
for (i = 0, Size = 0; i < FILE_HDR_SIZE; i++) {
|
Size = (Ptr[i] - '0') + (Size * 10);
|
}
|
|
return Size;
|
}
|
|
EFI_STATUS
|
RdkHttpBoot (
|
VOID
|
)
|
{
|
EFI_STATUS Status;
|
VOID *FilePtr;
|
UINT8 *FileBuffer;
|
UINT16 *Uri;
|
UINTN FileSize;
|
UINTN LoopIndex;
|
UINTN Size;
|
CONST CHAR16 *DtbPath;
|
CONST CHAR16 *ImagePath;
|
CONST CHAR16 *ServerUrlPath;
|
|
Status = GetRdkVariable (L"URL", &ServerUrlPath);
|
ASSERT_EFI_ERROR (Status);
|
|
// Get the Server name stored in file Server.url
|
Status = RdkReadFile (ServerUrlPath, (VOID **)&FileBuffer, &FileSize);
|
ASSERT_EFI_ERROR (Status);
|
|
Uri = AllocateZeroPool (sizeof(*Uri) * (FileSize + 1));
|
if (Uri == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
ASSERT_EFI_ERROR (Status);
|
}
|
|
for(LoopIndex = 0; LoopIndex < FileSize; LoopIndex++) {
|
Uri[LoopIndex] = FileBuffer[LoopIndex];
|
}
|
|
if(FileBuffer[FileSize-1] == '\n') {
|
Uri[FileSize-1] = '\0';
|
}
|
|
FreePool (FileBuffer);
|
FileBuffer=NULL;
|
|
// Disable watchdog
|
Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_WARN, \
|
"HttpBoot: Couldn't disable watchdog timer: %r\n", Status));
|
}
|
|
// Get the File from server using it's URI
|
Status = HttpGetImage (Uri, &FileBuffer, &FileSize);
|
ASSERT_EFI_ERROR (Status);
|
|
// Write the received image to flash
|
FilePtr = FileBuffer;
|
Size = ParseHeader (FilePtr);
|
FilePtr += FILE_HDR_SIZE;
|
Status = PartitionWrite ((CHAR8 *)FixedPcdGetPtr (\
|
PcdRdkSystemPartitionName), FilePtr, Size);
|
ASSERT_EFI_ERROR (Status);
|
|
FilePtr += Size;
|
Size = ParseHeader (FilePtr);
|
FilePtr += FILE_HDR_SIZE;
|
Status = GetRdkVariable (L"IMAGE", &ImagePath);
|
ASSERT_EFI_ERROR (Status);
|
Status = RdkWriteFile (ImagePath, &FilePtr, &Size);
|
ASSERT_EFI_ERROR (Status);
|
|
if ( FixedPcdGetBool (PcdDtbAvailable) ) {
|
FilePtr += Size;
|
Size = ParseHeader (FilePtr);
|
FilePtr += FILE_HDR_SIZE;
|
Status = GetRdkVariable (L"DTB", &DtbPath);
|
ASSERT_EFI_ERROR (Status);
|
Status = RdkWriteFile (DtbPath, &FilePtr, &Size);
|
ASSERT_EFI_ERROR (Status);
|
}
|
|
FreePool (FileBuffer);
|
FreePool (Uri);
|
|
return Status;
|
}
|