Implement network boot support in efiboot(8). This changes efiboot(8)
authorpatrick <patrick@openbsd.org>
Sun, 21 Jan 2018 21:35:34 +0000 (21:35 +0000)
committerpatrick <patrick@openbsd.org>
Sun, 21 Jan 2018 21:35:34 +0000 (21:35 +0000)
to recognize if it has been booted via PXE.  The new TFTP file system
layer will then use the matching PXE base code protocol handle to load
the files.  Since this uses the PXE base code protocol for abstraction
instead of the raw Simple Network protocol this will at this point not
work on u-boot based machines.

ok kettenis@

sys/arch/arm64/stand/efiboot/Makefile
sys/arch/arm64/stand/efiboot/conf.c
sys/arch/arm64/stand/efiboot/efiboot.c
sys/arch/arm64/stand/efiboot/efiboot.h
sys/arch/arm64/stand/efiboot/eficall.h
sys/arch/arm64/stand/efiboot/efipxe.c [new file with mode: 0644]
sys/arch/arm64/stand/efiboot/efipxe.h [new file with mode: 0644]

index 324528d..256d42c 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.2 2017/07/17 13:46:53 kettenis Exp $
+#      $OpenBSD: Makefile,v 1.3 2018/01/21 21:35:34 patrick Exp $
 
 NOMAN=         #
 
@@ -8,7 +8,8 @@ PROG=           BOOTAA64.EFI
 OBJFMT=                binary
 INSTALL_STRIP=
 BINDIR=                /usr/mdec
-SRCS=          start.S self_reloc.c efiboot.c conf.c exec.c efidev.c fdt.c
+SRCS=          start.S self_reloc.c efiboot.c conf.c exec.c efidev.c efipxe.c
+SRCS+=         fdt.c
 
 S=             ${.CURDIR}/../../../..
 EFIDIR=                ${S}/stand/efi
index 0fa0409..e504d62 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: conf.c,v 1.9 2017/09/08 05:36:51 deraadt Exp $        */
+/*     $OpenBSD: conf.c,v 1.10 2018/01/21 21:35:34 patrick Exp $       */
 
 /*
  * Copyright (c) 1996 Michael Shalayeff
 
 #include <sys/param.h>
 #include <lib/libsa/stand.h>
+#include <lib/libsa/tftp.h>
 #include <lib/libsa/ufs.h>
 #include <dev/cons.h>
 
 #include "efiboot.h"
 #include "efidev.h"
+#include "efipxe.h"
 
-const char version[] = "0.8";
+const char version[] = "0.9";
 int    debug = 0;
 
 struct fs_ops file_system[] = {
+       { tftp_open,   tftp_close,   tftp_read,   tftp_write,   tftp_seek,
+         tftp_stat,   tftp_readdir   },
        { ufs_open,    ufs_close,    ufs_read,    ufs_write,    ufs_seek,
          ufs_stat,    ufs_readdir    },
 };
 int nfsys = nitems(file_system);
 
 struct devsw   devsw[] = {
+       { "tftp", tftpstrategy, tftpopen, tftpclose, tftpioctl },
        { "sd", efistrategy, efiopen, eficlose, efiioctl },
 };
 int ndevs = nitems(devsw);
index a416ba0..c865419 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: efiboot.c,v 1.13 2017/08/23 18:03:54 kettenis Exp $   */
+/*     $OpenBSD: efiboot.c,v 1.14 2018/01/21 21:35:34 patrick Exp $    */
 
 /*
  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
@@ -31,6 +31,7 @@
 #include <stand/boot/cmd.h>
 
 #include "disk.h"
+#include "efiboot.h"
 #include "eficall.h"
 #include "fdt.h"
 #include "libsa.h"
@@ -53,8 +54,8 @@ static EFI_GUID                blkio_guid = BLOCK_IO_PROTOCOL;
 static EFI_GUID                 devp_guid = DEVICE_PATH_PROTOCOL;
 static EFI_GUID                 gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
 
-static int efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
-static int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
+int efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
+int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
 static void efi_heap_init(void);
 static void efi_memprobe_internal(void);
 static void efi_timer_init(void);
@@ -188,7 +189,7 @@ efi_diskprobe(void)
                    0, &sz, handles);
        }
        if (handles == NULL || EFI_ERROR(status))
-               panic("BS->LocateHandle() returns %d", status);
+               return;
 
        if (efi_bootdp != NULL)
                depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
@@ -231,7 +232,7 @@ efi_diskprobe(void)
  * Determine the number of nodes up to, but not including, the first
  * node of the specified type.
  */
-static int
+int
 efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
 {
        int     i;
@@ -244,7 +245,7 @@ efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
        return (-1);
 }
 
-static int
+int
 efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn)
 {
        int      i, cmp;
@@ -457,6 +458,7 @@ machdep(void)
 
        efi_timer_init();
        efi_diskprobe();
+       efi_pxeprobe();
 }
 
 void
@@ -540,7 +542,10 @@ getsecs(void)
 void
 devboot(dev_t dev, char *p)
 {
-       strlcpy(p, "sd0a", 5);
+       if (disk)
+               strlcpy(p, "sd0a", 5);
+       else
+               strlcpy(p, "tftp0a", 7);
 }
 
 int
@@ -641,7 +646,7 @@ devopen(struct open_file *f, const char *fname, char **file)
        if (error)
                return (error);
 
-       dp = &devsw[0];
+       dp = &devsw[dev];
        f->f_dev = dp;
 
        return (*dp->dv_open)(f, unit, part);
index 87d9255..8f69bb7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: efiboot.h,v 1.1 2016/12/17 23:38:33 patrick Exp $     */
+/*     $OpenBSD: efiboot.h,v 1.2 2018/01/21 21:35:34 patrick Exp $     */
 
 /*
  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
@@ -18,6 +18,7 @@
 
 void   efi_cleanup(void);
 void   efi_diskprobe(void);
+void   efi_pxeprobe(void);
 void   *efi_makebootargs(char *);
 void   efi_cons_probe(struct consdev *);
 void   efi_cons_init(struct consdev *);
index 6f4c216..dbf2e71 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: eficall.h,v 1.2 2016/12/30 09:43:18 yasuoka Exp $     */
+/*     $OpenBSD: eficall.h,v 1.3 2018/01/21 21:35:34 patrick Exp $     */
 
 /*
  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
@@ -44,10 +44,12 @@ extern uint64_t efi_call(int, void *, ...);
     efi_call(8, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8))
 #define        _call_9(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
     efi_call(9, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8), (_9))
+#define        _call_10(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
+    efi_call(10, (_func), (_1), (_2), (_3), (_4), (_5), (_6), (_7), (_8), (_9), (_10))
 
-#define _efi_call_fn(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9, _fn, ...) _fn
+#define _efi_call_fn(_func, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10,  _fn, ...) _fn
 
 #define        EFI_CALL(...)   \
-    _efi_call_fn(__VA_ARGS__, _call_9, _call_8, _call_7, _call_6, _call_5, \
-           _call_4, _call_3, _call_2, _call_1, _call_0)(__VA_ARGS__)
+    _efi_call_fn(__VA_ARGS__, _call_10, _call_9, _call_8,  _call_7, _call_6, \
+           _call_5, _call_4, _call_3, _call_2, _call_1, _call_0)(__VA_ARGS__)
 #endif
diff --git a/sys/arch/arm64/stand/efiboot/efipxe.c b/sys/arch/arm64/stand/efiboot/efipxe.c
new file mode 100644 (file)
index 0000000..d0684ea
--- /dev/null
@@ -0,0 +1,293 @@
+/*     $OpenBSD: efipxe.c,v 1.1 2018/01/21 21:35:34 patrick Exp $      */
+/*
+ * Copyright (c) 2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+
+#include <libsa.h>
+#include <lib/libsa/tftp.h>
+
+#include <efi.h>
+#include <efiapi.h>
+#include "eficall.h"
+#include "efiboot.h"
+#include "disk.h"
+
+extern EFI_BOOT_SERVICES       *BS;
+extern EFI_DEVICE_PATH         *efi_bootdp;
+
+static UINT8                    boothw[16];
+static EFI_IP_ADDRESS           bootip, servip;
+static EFI_GUID                         devp_guid = DEVICE_PATH_PROTOCOL;
+static EFI_GUID                         pxe_guid = EFI_PXE_BASE_CODE_PROTOCOL;
+static EFI_PXE_BASE_CODE       *PXE = NULL;
+
+extern int      efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
+extern int      efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
+
+/*
+ * TFTP initial probe.  This function discovers PXE handles and tries
+ * to figure out if there has already been a successfull PXE handshake.
+ * If so, set the PXE variable.
+ */
+void
+efi_pxeprobe(void)
+{
+       EFI_PXE_BASE_CODE *pxe;
+       EFI_DEVICE_PATH *dp0;
+       EFI_HANDLE *handles;
+       EFI_STATUS status;
+       UINTN nhandles;
+       int i, depth;
+
+       if (efi_bootdp == NULL)
+               return;
+
+       status = EFI_CALL(BS->LocateHandleBuffer, ByProtocol, &pxe_guid, NULL,
+           &nhandles, &handles);
+       if (status != EFI_SUCCESS)
+               return;
+
+       for (i = 0; i < nhandles; i++) {
+               EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp = NULL;
+
+               status = EFI_CALL(BS->HandleProtocol, handles[i],
+                   &devp_guid, (void **)&dp0);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
+               if (efi_device_path_ncmp(efi_bootdp, dp0, depth))
+                       continue;
+
+               status = EFI_CALL(BS->HandleProtocol, handles[i], &pxe_guid,
+                   (void **)&pxe);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               if (pxe->Mode == NULL)
+                       continue;
+
+               if (pxe->Mode->DhcpAckReceived) {
+                       dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET *)
+                           &pxe->Mode->DhcpAck;
+               }
+               if (pxe->Mode->PxeReplyReceived) {
+                       dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET *)
+                           &pxe->Mode->PxeReply;
+               }
+
+               if (dhcp) {
+                       memcpy(&bootip, dhcp->BootpYiAddr, sizeof(bootip));
+                       memcpy(&servip, dhcp->BootpSiAddr, sizeof(servip));
+                       memcpy(boothw, dhcp->BootpHwAddr, sizeof(boothw));
+                       PXE = pxe;
+                       break;
+               }
+       }
+}
+
+/*
+ * TFTP filesystem layer implementation.
+ */
+struct tftp_handle {
+       unsigned char   *inbuf; /* input buffer */
+       size_t           inbufsize;
+       off_t            inbufoff;
+};
+
+struct fs_ops tftp_fs = {
+       tftp_open, tftp_close, tftp_read, tftp_write, tftp_seek,
+       tftp_stat, tftp_readdir
+};
+
+int
+tftp_open(char *path, struct open_file *f)
+{
+       struct tftp_handle *tftpfile;
+       EFI_PHYSICAL_ADDRESS addr;
+       EFI_STATUS status;
+       UINT64 size;
+
+       if (PXE == NULL)
+               return ENXIO;
+
+       tftpfile = alloc(sizeof(*tftpfile));
+       if (tftpfile == NULL)
+               return ENOMEM;
+       memset(tftpfile, 0, sizeof(*tftpfile));
+
+       status = EFI_CALL(PXE->Mtftp, PXE, EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+           NULL, FALSE, &size, NULL, &servip, path, NULL, FALSE);
+       if (status != EFI_SUCCESS) {
+               free(tftpfile, sizeof(*tftpfile));
+               return ENOENT;
+       }
+       tftpfile->inbufsize = size;
+
+       status = EFI_CALL(BS->AllocatePages, AllocateAnyPages, EfiLoaderData,
+           EFI_SIZE_TO_PAGES(tftpfile->inbufsize), &addr);
+       if (status != EFI_SUCCESS) {
+               free(tftpfile, sizeof(*tftpfile));
+               return ENOMEM;
+       }
+       tftpfile->inbuf = (unsigned char *)((paddr_t)addr);
+
+       status = EFI_CALL(PXE->Mtftp, PXE, EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+           tftpfile->inbuf, FALSE, &size, NULL, &servip, path, NULL, FALSE);
+       if (status != EFI_SUCCESS) {
+               free(tftpfile, sizeof(*tftpfile));
+               return ENXIO;
+       }
+
+       f->f_fsdata = tftpfile;
+       return 0;
+}
+
+int
+tftp_close(struct open_file *f)
+{
+       struct tftp_handle *tftpfile = f->f_fsdata;
+
+       EFI_CALL(BS->FreePages, (paddr_t)tftpfile->inbuf,
+           EFI_SIZE_TO_PAGES(tftpfile->inbufsize));
+       free(tftpfile, sizeof(*tftpfile));
+       return 0;
+}
+
+int
+tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid)
+{
+       struct tftp_handle *tftpfile = f->f_fsdata;
+       size_t toread;
+
+       if (size > tftpfile->inbufsize - tftpfile->inbufoff)
+               toread = tftpfile->inbufsize - tftpfile->inbufoff;
+       else
+               toread = size;
+
+       if (toread == 0) {
+               if (resid != NULL)
+                       *resid = 0;
+               return (0);
+       }
+
+       memcpy(addr, tftpfile->inbuf + tftpfile->inbufoff, toread);
+       tftpfile->inbufoff += toread;
+
+       if (resid != NULL)
+               *resid = size - toread;
+       return 0;
+}
+
+int
+tftp_write(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+       return EROFS;
+}
+
+off_t
+tftp_seek(struct open_file *f, off_t offset, int where)
+{
+       struct tftp_handle *tftpfile = f->f_fsdata;
+
+       switch(where) {
+       case SEEK_CUR:
+               if (tftpfile->inbufoff + offset < 0) {
+                       errno = EOFFSET;
+                       break;
+               }
+               tftpfile->inbufoff += offset;
+               return (tftpfile->inbufoff);
+       case SEEK_SET:
+               if (offset < 0 || offset > tftpfile->inbufsize) {
+                       errno = EOFFSET;
+                       break;
+               }
+               tftpfile->inbufoff = offset;
+               return (tftpfile->inbufoff);
+       case SEEK_END:
+               tftpfile->inbufoff = tftpfile->inbufsize;
+               return (tftpfile->inbufoff);
+       default:
+               errno = EINVAL;
+       }
+       return((off_t)-1);
+}
+
+int
+tftp_stat(struct open_file *f, struct stat *sb)
+{
+       struct tftp_handle *tftpfile = f->f_fsdata;
+
+       sb->st_mode = 0444;
+       sb->st_nlink = 1;
+       sb->st_uid = 0;
+       sb->st_gid = 0;
+       sb->st_size = tftpfile->inbufsize;
+
+       return 0;
+}
+
+int
+tftp_readdir(struct open_file *f, char *name)
+{
+       return EOPNOTSUPP;
+}
+
+/*
+ * Dummy TFTP network device.
+ */
+int
+tftpopen(struct open_file *f, ...)
+{
+       u_int unit, part;
+       va_list ap;
+
+       va_start(ap, f);
+       unit = va_arg(ap, u_int);
+       part = va_arg(ap, u_int);
+       va_end(ap);
+
+       /* No PXE set -> no PXE available */
+       if (PXE == NULL)
+               return 1;
+
+       if (unit != 0)
+               return 1;
+
+       return 0;
+}
+
+int
+tftpclose(struct open_file *f)
+{
+       return 0;
+}
+
+int
+tftpioctl(struct open_file *f, u_long cmd, void *data)
+{
+       return EOPNOTSUPP;
+}
+
+int
+tftpstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
+       size_t *rsize)
+{
+       return EOPNOTSUPP;
+}
diff --git a/sys/arch/arm64/stand/efiboot/efipxe.h b/sys/arch/arm64/stand/efiboot/efipxe.h
new file mode 100644 (file)
index 0000000..3ff18e0
--- /dev/null
@@ -0,0 +1,21 @@
+/*     $OpenBSD: efipxe.h,v 1.1 2018/01/21 21:35:34 patrick Exp $      */
+/*
+ * Copyright (c) 2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+int tftpopen(struct open_file *, ...);
+int tftpclose(struct open_file *);
+int tftpioctl(struct open_file *, u_long, void *);
+int tftpstrategy(void *, int, daddr32_t, size_t, void *, size_t *);