On Fri, Nov 24, 2017 at 04:48:08PM -0700, Andrew Hewus Fresh wrote:
> A version of this patch that applies cleanly lets me autoinstall
> OpenBSD on a FreeNAS under bhyve.
> So this seems useful and works for me.
> 
> Anyway, I wrote down notes of what I had to do.  I should probably try
> vmm on this hardware again, but time constraints and all that.
> 
> https://gist.github.com/afresh1/804fc0a315ee41e88a24f1aa5e2d3552

Updated diff, ok?

diff --git a/sys/arch/amd64/stand/efiboot/Makefile.common 
b/sys/arch/amd64/stand/efiboot/Makefile.common
index 6dc1cdebd4d..40f5cd2282d 100644
--- a/sys/arch/amd64/stand/efiboot/Makefile.common
+++ b/sys/arch/amd64/stand/efiboot/Makefile.common
@@ -24,7 +24,7 @@ AFLAGS+=      -pipe -fPIC
 
 .PATH: ${.CURDIR}/..
 SRCS+= self_reloc.c
-SRCS+= efiboot.c efidev.c
+SRCS+= efiboot.c efidev.c efipxe.c
 SRCS+= conf.c
 
 .PATH: ${S}/stand/boot
diff --git a/sys/arch/amd64/stand/efiboot/conf.c 
b/sys/arch/amd64/stand/efiboot/conf.c
index 9d825543ec0..befac5a4210 100644
--- a/sys/arch/amd64/stand/efiboot/conf.c
+++ b/sys/arch/amd64/stand/efiboot/conf.c
@@ -30,12 +30,14 @@
 #include <sys/disklabel.h>
 #include <libsa.h>
 #include <lib/libsa/ufs.h>
+#include <lib/libsa/tftp.h>
 #include <lib/libsa/cd9660.h>
 #include <dev/cons.h>
 
 #include "disk.h"
 #include "efiboot.h"
 #include "efidev.h"
+#include "efipxe.h"
 
 const char version[] = "3.35";
 
@@ -50,7 +52,7 @@ void (*i386_probe1[])(void) = {
        cninit, efi_memprobe
 };
 void (*i386_probe2[])(void) = {
-       efi_diskprobe, diskprobe
+       efi_pxeprobe, efi_diskprobe, diskprobe
 };
 
 struct i386_boot_probes probe_list[] = {
@@ -61,6 +63,8 @@ int nibprobes = nitems(probe_list);
 
 
 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    },
        { cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek,
@@ -75,10 +79,8 @@ struct fs_ops file_system[] = {
 int nfsys = nitems(file_system);
 
 struct devsw   devsw[] = {
-       { "EFI", efistrategy, efiopen, eficlose, efiioctl },
-#if 0
        { "TFTP", tftpstrategy, tftpopen, tftpclose, tftpioctl },
-#endif
+       { "EFI", efistrategy, efiopen, eficlose, efiioctl },
 };
 int ndevs = nitems(devsw);
 
diff --git a/sys/arch/amd64/stand/efiboot/efiboot.c 
b/sys/arch/amd64/stand/efiboot/efiboot.c
index 90bd85576fd..4bbc6bdb5e5 100644
--- a/sys/arch/amd64/stand/efiboot/efiboot.c
+++ b/sys/arch/amd64/stand/efiboot/efiboot.c
@@ -52,9 +52,8 @@ static EFI_GUID                blkio_guid = BLOCK_IO_PROTOCOL;
 static EFI_GUID                 devp_guid = DEVICE_PATH_PROTOCOL;
 u_long                  efi_loadaddr;
 
-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_video_init(void);
@@ -101,6 +100,11 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
                                    ? 0x1e0 : 0x80;
                                efi_bootdp = dp0;
                                break;
+                       } else if (DevicePathType(dp) == MESSAGING_DEVICE_PATH&&
+                           DevicePathSubType(dp) == MSG_MAC_ADDR_DP) {
+                               bios_bootdev = 0x0;
+                               efi_bootdp = dp0;
+                               break;
                        }
                }
        }
@@ -233,7 +237,7 @@ next:
  * 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;
@@ -246,7 +250,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;
diff --git a/sys/arch/amd64/stand/efiboot/efiboot.h 
b/sys/arch/amd64/stand/efiboot/efiboot.h
index 09ba95ebe9d..e1f726551f3 100644
--- a/sys/arch/amd64/stand/efiboot/efiboot.h
+++ b/sys/arch/amd64/stand/efiboot/efiboot.h
@@ -21,6 +21,7 @@ void   efi_cons_probe(struct consdev *);
 void    efi_memprobe(void);
 void    efi_hardprobe(void);
 void    efi_diskprobe(void);
+void    efi_pxeprobe(void);
 void    efi_cons_init(struct consdev *);
 int     efi_cons_getc(dev_t);
 void    efi_cons_putc(dev_t, int);
diff --git a/sys/arch/amd64/stand/efiboot/eficall.h 
b/sys/arch/amd64/stand/efiboot/eficall.h
index 38b903efa20..f8cdd5c1228 100644
--- a/sys/arch/amd64/stand/efiboot/eficall.h
+++ b/sys/arch/amd64/stand/efiboot/eficall.h
@@ -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/amd64/stand/efiboot/efipxe.c 
b/sys/arch/amd64/stand/efiboot/efipxe.c
new file mode 100644
index 00000000000..1c0a21dc80e
--- /dev/null
+++ b/sys/arch/amd64/stand/efiboot/efipxe.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2017 Patrick Wildt <patr...@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 <machine/biosvar.h>
+
+#include <libsa.h>
+#include <lib/libsa/tftp.h>
+
+#include "disk.h"
+
+#include <efi.h>
+#include <efiapi.h>
+#include "eficall.h"
+#include "efiboot.h"
+
+extern EFI_BOOT_SERVICES       *BS;
+extern EFI_DEVICE_PATH         *efi_bootdp;
+
+extern char                    *bootmac;
+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;
+
+int     efi_device_path_depth(EFI_DEVICE_PATH *dp, int);
+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));
+                       bootmac = 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, ...)
+{
+       char **fname, *p;
+       va_list ap;
+
+       va_start(ap, f);
+       fname = va_arg(ap, char **);
+       va_end(ap);
+
+       /* No PXE set -> no PXE available */
+       if (PXE == NULL)
+               return 1;
+
+       /* Parse tftp:bsd into "tftp" and "bsd" */
+       for (p = *fname; *p != ':' && *p != '\0'; p++)
+               ;
+       if (*p != ':')
+               return 1;
+       if (strncmp(*fname, "tftp", p - *fname) != 0)
+               return 1;
+
+       *fname = p + 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/amd64/stand/efiboot/efipxe.h 
b/sys/arch/amd64/stand/efiboot/efipxe.h
new file mode 100644
index 00000000000..a61358e555e
--- /dev/null
+++ b/sys/arch/amd64/stand/efiboot/efipxe.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Patrick Wildt <patr...@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 *);
diff --git a/sys/arch/amd64/stand/libsa/dev_i386.c 
b/sys/arch/amd64/stand/libsa/dev_i386.c
index 5d8e389bf82..a4bf6d86e15 100644
--- a/sys/arch/amd64/stand/libsa/dev_i386.c
+++ b/sys/arch/amd64/stand/libsa/dev_i386.c
@@ -108,6 +108,17 @@ devboot(dev_t bootdev, char *p)
        int sr_boot_vol = -1;
        int part_type = FS_UNUSED;
 
+#ifdef EFIBOOT
+       if (!bootdev) {
+               *p++ = 't';
+               *p++ = 'f';
+               *p++ = 't';
+               *p++ = 'p';
+               *p = '\0';
+               return;
+       }
+#endif
+
 #ifdef SOFTRAID
        /*
         * Determine the partition type for the 'a' partition of the

Reply via email to