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."