+// SPDX-License-Identifier: BSD-2-Clause-Patent
+
/*
* netboot - trivial UEFI first-stage bootloader netboot support
*
- * Copyright 2012 Red Hat, Inc <mjg@redhat.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.
+ * Copyright Red Hat, Inc
+ * Author: Matthew Garrett
*
* 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 <string.h>
#include "shim.h"
-#include "netboot.h"
-#include "str.h"
#define ntohs(x) __builtin_bswap16(x) /* supported both by GCC and clang */
#define htons(x) ntohs(x)
UINT8 Data[1];
} EFI_DHCP6_PACKET_OPTION;
-static CHAR8 *
-translate_slashes(char *str)
-{
- int i;
- int j;
- if (str == NULL)
- return (CHAR8 *)str;
-
- for (i = 0, j = 0; str[i] != '\0'; i++, j++) {
- if (str[i] == '\\') {
- str[j] = '/';
- if (str[i+1] == '\\')
- i++;
- }
- }
- return (CHAR8 *)str;
-}
-
/*
* usingNetboot
* Returns TRUE if we identify a protocol that is enabled and Providing us with
*/
BOOLEAN findNetboot(EFI_HANDLE device)
{
- EFI_STATUS status;
+ EFI_STATUS efi_status;
- status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
- &PxeBaseCodeProtocol, (VOID **)&pxe);
- if (status != EFI_SUCCESS) {
+ efi_status = BS->HandleProtocol(device, &PxeBaseCodeProtocol,
+ (VOID **) &pxe);
+ if (EFI_ERROR(efi_status)) {
pxe = NULL;
return FALSE;
}
CHAR8 *start, *end;
CHAR8 ip6str[40];
CHAR8 ip6inv[16];
- CHAR8 *template = (CHAR8 *)translate_slashes(DEFAULT_LOADER_CHAR);
+ CHAR8 template[sizeof DEFAULT_LOADER_CHAR];
+
+ translate_slashes(template, DEFAULT_LOADER_CHAR);
// to check against str2ip6() errors
memset(ip6inv, 0, sizeof(ip6inv));
- if (strncmp((UINT8 *)url, (UINT8 *)"tftp://", 7)) {
- Print(L"URLS MUST START WITH tftp://\n");
+ if (strncmp((const char *)url, (const char *)"tftp://", 7)) {
+ console_print(L"URLS MUST START WITH tftp://\n");
return FALSE;
}
start = url + 7;
if (*start != '[') {
- Print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
+ console_print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
return FALSE;
}
while ((*end != '\0') && (*end != ']')) {
end++;
if (end - start >= (int)sizeof(ip6str)) {
- Print(L"TFTP URL includes malformed IPv6 address\n");
+ console_print(L"TFTP URL includes malformed IPv6 address\n");
return FALSE;
}
}
if (*end == '\0') {
- Print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
+ console_print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
return FALSE;
}
memset(ip6str, 0, sizeof(ip6str));
static EFI_STATUS parseDhcp4()
{
- CHAR8 *template = (CHAR8 *)translate_slashes(DEFAULT_LOADER_CHAR);
- INTN template_len = strlen(template) + 1;
+ CHAR8 template[sizeof DEFAULT_LOADER_CHAR];
+ INTN template_len;
+ UINTN template_ofs = 0;
+ EFI_PXE_BASE_CODE_DHCPV4_PACKET* pkt_v4 = (EFI_PXE_BASE_CODE_DHCPV4_PACKET *)&pxe->Mode->DhcpAck.Dhcpv4;
+
+ translate_slashes(template, DEFAULT_LOADER_CHAR);
+ template_len = strlen(template) + 1;
+
+ if(pxe->Mode->ProxyOfferReceived) {
+ /*
+ * Proxy should not have precedence. Check if DhcpAck
+ * contained boot info.
+ */
+ if(pxe->Mode->DhcpAck.Dhcpv4.BootpBootFile[0] == '\0')
+ pkt_v4 = &pxe->Mode->ProxyOffer.Dhcpv4;
+ }
- INTN dir_len = strnlena(pxe->Mode->DhcpAck.Dhcpv4.BootpBootFile, 127);
+ if(pxe->Mode->PxeReplyReceived) {
+ /*
+ * If we have no bootinfo yet search for it in the PxeReply.
+ * Some mainboards run into this when the server uses boot menus
+ */
+ if(pkt_v4->BootpBootFile[0] == '\0' && pxe->Mode->PxeReply.Dhcpv4.BootpBootFile[0] != '\0')
+ pkt_v4 = &pxe->Mode->PxeReply.Dhcpv4;
+ }
+
+ INTN dir_len = strnlen((CHAR8 *)pkt_v4->BootpBootFile, 127);
INTN i;
- UINT8 *dir = pxe->Mode->DhcpAck.Dhcpv4.BootpBootFile;
+ UINT8 *dir = pkt_v4->BootpBootFile;
for (i = dir_len; i >= 0; i--) {
if (dir[i] == '/')
return EFI_OUT_OF_RESOURCES;
if (dir_len > 0) {
- strncpya(full_path, dir, dir_len);
+ strncpy(full_path, (CHAR8 *)dir, dir_len);
if (full_path[dir_len-1] == '/' && template[0] == '/')
full_path[dir_len-1] = '\0';
}
if (dir_len == 0 && dir[0] != '/' && template[0] == '/')
- template++;
- strcata(full_path, template);
- memcpy(&tftp_addr.v4, pxe->Mode->DhcpAck.Dhcpv4.BootpSiAddr, 4);
+ template_ofs++;
+ strcat(full_path, template + template_ofs);
+ memcpy(&tftp_addr.v4, pkt_v4->BootpSiAddr, 4);
return EFI_SUCCESS;
}
-EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle)
+EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle UNUSED)
{
- EFI_STATUS rc;
+ EFI_STATUS efi_status;
if (!pxe)
return EFI_NOT_READY;
* if its ipv4 or ipv6
*/
if (pxe->Mode->UsingIpv6){
- rc = parseDhcp6();
+ efi_status = parseDhcp6();
} else
- rc = parseDhcp4();
- return rc;
+ efi_status = parseDhcp4();
+ return efi_status;
}
-EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINT64 *bufsiz)
+EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle UNUSED, VOID **buffer, UINT64 *bufsiz)
{
- EFI_STATUS rc;
+ EFI_STATUS efi_status;
EFI_PXE_BASE_CODE_TFTP_OPCODE read = EFI_PXE_BASE_CODE_TFTP_READ_FILE;
BOOLEAN overwrite = FALSE;
BOOLEAN nobuffer = FALSE;
UINTN blksz = 512;
- Print(L"Fetching Netboot Image\n");
+ console_print(L"Fetching Netboot Image\n");
if (*buffer == NULL) {
*buffer = AllocatePool(4096 * 1024);
if (!*buffer)
- return EFI_OUT_OF_RESOURCES;
+ return EFI_OUT_OF_RESOURCES;
*bufsiz = 4096 * 1024;
}
try_again:
- rc = uefi_call_wrapper(pxe->Mtftp, 10, pxe, read, *buffer, overwrite,
- bufsiz, &blksz, &tftp_addr, full_path, NULL, nobuffer);
-
- if (rc == EFI_BUFFER_TOO_SMALL) {
+ efi_status = pxe->Mtftp(pxe, read, *buffer, overwrite, bufsiz, &blksz,
+ &tftp_addr, (UINT8 *)full_path, NULL, nobuffer);
+ if (efi_status == EFI_BUFFER_TOO_SMALL) {
/* try again, doubling buf size */
*bufsiz *= 2;
FreePool(*buffer);
goto try_again;
}
- if (rc != EFI_SUCCESS && *buffer) {
+ if (EFI_ERROR(efi_status) && *buffer) {
FreePool(*buffer);
}
- return rc;
+ return efi_status;
}