[syslinux:master] pxe: Add support for embedded options in EFI

syslinux-bot for H. Peter Anvin hpa at zytor.com
Mon Jun 16 20:27:04 PDT 2014


Commit-ID:  44e01ff5852eae227e84b7ac3b4d7a7bf8574c3c
Gitweb:     http://www.syslinux.org/commit/44e01ff5852eae227e84b7ac3b4d7a7bf8574c3c
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Mon, 16 Jun 2014 20:25:02 -0700
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Mon, 16 Jun 2014 20:25:02 -0700

pxe: Add support for embedded options in EFI

For EFI, rather than mucking with the PECOFF or ELF headers (we have
both!) just use a fixed-size buffer embedded in the image with a large
magic number that can be scanned for safely.

Signed-off-by: H. Peter Anvin <hpa at zytor.com>

---
 efi/pxe.c              |  35 +++++++++++++++++
 utils/pxelinux-options | 101 ++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 113 insertions(+), 23 deletions(-)

diff --git a/efi/pxe.c b/efi/pxe.c
index 62fddb0..0c9fb06 100644
--- a/efi/pxe.c
+++ b/efi/pxe.c
@@ -65,6 +65,28 @@ int pxe_init(bool quiet)
     return 0;
 }
 
+#define EDHCP_BUF_LEN 8192
+
+struct embedded_dhcp_options {
+    uint32_t magic[4];
+    uint32_t bdhcp_len;
+    uint32_t adhcp_len;
+    uint32_t buffer_size;
+    uint32_t reserved;
+    uint8_t  dhcp_data[EDHCP_BUF_LEN];
+} __attribute__((aligned(16)));
+
+struct embedded_dhcp_options embedded_dhcp_options =
+{
+    .magic[0] = 0x2a171ead,
+    .magic[1] = 0x0600e65e,
+    .magic[2] = 0x4025a4e4,
+    .magic[3] = 0x42388fc8,
+    .bdhcp_len = 0,
+    .adhcp_len = 0,
+    .buffer_size = EDHCP_BUF_LEN,
+};
+
 void net_parse_dhcp(void)
 {
     EFI_PXE_BASE_CODE_MODE *mode;
@@ -92,6 +114,12 @@ void net_parse_dhcp(void)
     mode = bc->Mode;
 
     /*
+     * Parse any "before" hardcoded options
+     */
+    parse_dhcp_options(embedded_dhcp_options.dhcp_data,
+		       embedded_dhcp_options.bdhcp_len, 0);
+
+    /*
      * Get the DHCP client identifiers (query info 1)
      */
     Print(L"Getting cached packet ");
@@ -129,6 +157,13 @@ void net_parse_dhcp(void)
     parse_dhcp(&mode->PxeReply.Dhcpv4, pkt_len);
     Print(L"\n");
 
+    /*
+     * Parse any "after" hardcoded options
+     */
+    parse_dhcp_options(embedded_dhcp_options.dhcp_data +
+		       embedded_dhcp_options.bdhcp_len,
+		       embedded_dhcp_options.adhcp_len, 0);
+
     ip = IPInfo.myip;
     sprintf(dst, "%u.%u.%u.%u",
         ((const uint8_t *)&ip)[0],
diff --git a/utils/pxelinux-options b/utils/pxelinux-options
index ab7075b..dfd98cf 100755
--- a/utils/pxelinux-options
+++ b/utils/pxelinux-options
@@ -199,21 +199,56 @@ sub read_optsets($)
     my($file) = @_;
     my $data, $bdata, $adata;
     my $patch_start = (stat($file))[7];
+    my $hdroffset = 0;		# 0 means non-deep-embedded
+    my $bufsize = 0;
+    my $junk;
+    my %hdr;
 
-    return undef unless (seek($file, 8, SEEK_SET));
-    return undef unless (read($file, $data, 7*4) == 7*4);
+    return undef unless (seek($file, 0, SEEK_SET));
+    return undef unless (read($file, $data, 48) == 48);
 
-    my($magic, $len, $flags, $boff, $blen, $aoff, $alen)
-	= unpack("VVVVVVV", $data);
-    return undef if ($magic != 0x2983c8ac);
-    return undef if ($len < 7*4);
+    my($mzmagic, $junk, $magic, $len, $flags, $boff, $blen, $aoff, $alen)
+	= unpack("va[6]VVVVVVV", $data);
+
+    if ($mzmagic == 0x5a4d) {
+	# It is an EFI file... search for the magic number
+	$hdroffset = 48;
+	my $magic = pack("VVVV", 0x2a171ead, 0x0600e65e,
+			 0x4025a4e4, 0x42388fc8);
+
+	while (1) {
+	    return undef unless (read($file, $data, 16) == 16);
+	    last if ($data eq $magic);
+
+	    $hdroffset += 16;
+	}
+
+	return undef unless (read($file, $data, 16) == 16);
+	($blen, $alen, $bufsize, $junk) = unpack("VVVV", $data);
+
+	printf STDERR "EFI: offset = 0x%x, blen = %d, alen = %d, bufsize = %d\n", $hdroffset, $blen, $alen, $bufsize;
+
+	$patch_start = $boff = $hdroffset + 32;
+	$aoff = $boff + $blen;
+
+	$hdr{'deep'} = 1;
+	$hdr{'bufsize'} = $bufsize;
+	$hdr{'hdroffset'} = $hdroffset;
+    } else {
+	# It is a BIOS PXE file
+
+	return undef if ($magic != 0x2983c8ac);
+	return undef if ($len < 7*4);
+
+	$hdr{'deep'} = 0;
+    }
 
     if ($blen == 0) {
 	$bdata = '';
     } else {
 	return undef unless (seek($file, $boff, SEEK_SET));
 	return undef unless (read($file, $bdata, $blen) == $blen);
-	$patch_start = $boff if ($boff < $patch_start);
+	$patch_start = $boff if ($boff < patch_start);
     }
 
     if ($alen == 0) {
@@ -224,37 +259,57 @@ sub read_optsets($)
 	$patch_start = $aoff if ($aoff < $patch_start);
     }
 
-    return ($patch_start, $bdata, $adata);
+    $hdr{'patch_start'} = $patch_start;
+
+    return (\%hdr, $bdata, $adata);
 }
 
-sub write_optsets($$@)
+sub write_optsets($$$@)
 {
-    my($file, $patch_start, $bdata, $adata) = @_;
+    my($file, $hdr, $bdata, $adata) = @_;
     my $boff = 0;
     my $aoff = 0;
+    my $bufsize = 0;
+    my $patch_start = $hdr->{'patch_start'};
+    my $len;
+
+    $bdata .= "\xff" unless ($bdata eq '');
+    $adata .= "\xff" unless ($adata eq '');
+
+    $len = length($bdata) + length($adata);
+
+    if (defined($hdr->{'bufsize'})) {
+	return undef unless ($len <= $hdr->{'bufsize'});
+    }
+
+    return undef unless (seek($file, $patch_start, SEEK_SET));
 
-    if (length($bdata) > 0) {
-	$bdata .= "\xff";
+    if (length($bdata)) {
 	$boff = $patch_start;
-	return undef unless (seek($file, $boff, SEEK_SET));
 	return undef unless (print $file $bdata);
 	$patch_start += length($bdata);
     }
 
-    if (length($adata) > 0) {
-	$adata .= "\xff";
+    if (length($adata)) {
 	$aoff = $patch_start;
-	return undef unless (seek($file, $aoff, SEEK_SET));
 	return undef unless (print $file $adata);
 	$patch_start += length($adata);
     }
 
-    my $hdr = pack("VVVV", $boff, length($bdata), $aoff, length($adata));
+    if ($hdr->{'deep'}) {
+	return undef unless (print $file "\0" x ($hdr->{'bufsize'} - $len));
+	return undef unless (seek($file, $hdr->{'hdroffset'} + 16, SEEK_SET));
+	my $hdr = pack("VV", length($bdata), length($adata));
+	return undef unless (print $file $hdr);
+    } else {
+	my $hdr = pack("VVVV", $boff, length($bdata), $aoff, length($adata));
 
-    return undef unless (seek($file, 8+3*4, SEEK_SET));
-    return undef unless (print $file $hdr);
+	return undef unless (seek($file, 8+3*4, SEEK_SET));
+	return undef unless (print $file $hdr);
+
+	truncate($file, $patch_start);
+    }
 
-    truncate($file, $patch_start);
     return 1;
 }
 
@@ -468,8 +523,8 @@ $mode = $no_write ? '<' : '+<';
 
 open(FILE, $mode, $file)
     or die "$0: cannot open: $file: $!\n";
-($patch_start, @data) = read_optsets(\*FILE);
-if (!defined($patch_start)) {
+($hdrinfo, @data) = read_optsets(\*FILE);
+if (!defined($hdrinfo)) {
     die "$0: $file: patch block not found or file corrupt\n";
 }
 
@@ -490,7 +545,7 @@ if ($list) {
 }
 
 if (!$no_write) {
-    if (!write_optsets(\*FILE, $patch_start, @data)) {
+    if (!write_optsets(\*FILE, $hdrinfo, @data)) {
 	die "$0: $file: failed to write options: $!\n";
     }
 }


More information about the Syslinux-commits mailing list