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

On Thu, Jun 08, 2017 at 05:16:08PM +0200, Patrick Wildt wrote:
> On Thu, Jun 08, 2017 at 11:42:44PM +0900, YASUOKA Masahiko wrote:
> > Hi,
> > 
> > Tested the diff.  It works fine.
> > 
> > On Wed, 7 Jun 2017 20:23:43 +0200
> > Patrick Wildt <patr...@blueri.se> wrote:
> > > 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.
> > 
> > What the diff is missing is passing the mac address of booted NIC as
> > "bootmac" parameter?
> > 
> > --yasuoka
> > 
> 
> Does this do what you want it to do?
> 
> 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..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
> 

-- 
andrew - http://afresh1.com

Computer analyst to programmer:
               "You start coding. I'll go find out what they want."

Reply via email to