On Wed, Jun 07, 2017 at 03:58:01PM +0200, Sven-Volker Nowarra wrote:
> Hi,
> 
> I setup a PXE boot server, to load my different clients. Now I have 2 single 
> EFI devices, which do not support PXE (both are Macminis). They prefer "EFI" 
> files. Both are capable running 64bit software, but require 32bit EFI 
> loaders. So I added a line in my dhcpd.conf to supply “BOOTIA32.EFI” file.  
> It get’s loaded via tftp, and displays on the clients console the well known 
> bsd prompt for booting: 
> 
>    probing: pc0 mem 1576K 64K 2015M 11M 64K 52KI
>    disk: hd0* hd1*
>    >> OpenBSD/amd64 BOOTIA32 3.30
>    open(hd0a:/etc/boot.conf): invalid argument
>    boot> _
> 
> Here the client stops, for sure there is no harddrive. Somehow the efiboot 
> file does not provide the option to load the kernel via network, only hd0 or 
> hd1 is offered. I looked up the files in 
> /sys/arch/amd64/stand/efiboot/efiboot.c and the header files, but couldn't 
> find the references to IPv4 (or IPv6) booting, though the header files in 
> /usr/src/sys/stand/efi/include contain the structures for IPv4_DEVICE_PATH. 
> Can someone point me into the right direction?  
> 
> ---
> 
> On dhcp server: Macs boot differently over the net with dhcp and bootp. I use 
> a class definition, and inside s.th <http://s.th/>. like this:
> 
> class "AppleNBI-i386" {
> match if substring (option vendor-class-identifier, 0, 14) = "AAPLBSDPC/i386";
> option dhcp-parameter-request-list 1,3,17,43,60;
> if (option dhcp-message-type = 1)
>    {
>    option vendor-class-identifier "AAPLBSDPC/i386";
>    }
> if (option dhcp-message-type = 1)
>    {
>    option vendor-encapsulated-options 08:04:81:00:00:67;
>    }
> if (substring (option vendor-class-identifier, 15, 10) = "Macmini1,1")
>    {
>    log(info, concat("DHCPd: Macmini1,1: vendor-class-identifier = ", option 
> vendor-class-identifier));
>    filename "amd64/BOOTIA32.EFI";
>    }
> ...
> 
> So I didn't need to setup dhcp with "option arch code 93", as described here:
> http://marc.info/?l=openbsd-tech&m=148517258726719&w=2
> 

Do you want it to run diskless with root on NFS?  Well, I haven't
implemented that, but here's a diff that should allow you to load
the kernel from a TFTP server.

I have a similar diff on arm64 (which probably doesn't work with
u-boot's EFI API, but works on EDK2 machines) which I use for testing
kernels more easily.

I'm not sure how helpful this feature is for most people.  It's probably
nice for developers who want to boot different kernels over network, but
so far it does not support root on NFS, or only accidentally.

Patrick

diff --git a/sys/arch/amd64/stand/efiboot/Makefile.common 
b/sys/arch/amd64/stand/efiboot/Makefile.common
index d821e0bc39a..7742e201683 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 3b2059e414f..55ab425fab2 100644
--- a/sys/arch/amd64/stand/efiboot/conf.c
+++ b/sys/arch/amd64/stand/efiboot/conf.c
@@ -31,12 +31,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.33";
 
@@ -51,7 +53,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[] = {
@@ -62,6 +64,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,
@@ -76,10 +80,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 9b6d5fc00fd..d753ffbf919 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);
@@ -96,6 +95,11 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
                                bios_bootdev = 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;
                        }
                }
        }
@@ -215,7 +219,7 @@ next:
        free(handles, sz);
 }
 
-static int
+int
 efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype)
 {
        int     i;
@@ -228,7 +232,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 8a07d5b46b3..ac50556748c 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..47463f01989
--- /dev/null
+++ b/sys/arch/amd64/stand/efiboot/efipxe.c
@@ -0,0 +1,296 @@
+/*
+ * 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 <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;
+
+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));
+                       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