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