From 33e5575ab75c73b5ea838e568b972888474aa25d Mon Sep 17 00:00:00 2001 From: patrick Date: Sun, 21 Jan 2018 21:35:34 +0000 Subject: [PATCH] Implement network boot support in efiboot(8). This changes efiboot(8) to recognize if it has been booted via PXE. The new TFTP file system layer will then use the matching PXE base code protocol handle to load the files. Since this uses the PXE base code protocol for abstraction instead of the raw Simple Network protocol this will at this point not work on u-boot based machines. ok kettenis@ --- sys/arch/arm64/stand/efiboot/Makefile | 5 +- sys/arch/arm64/stand/efiboot/conf.c | 9 +- sys/arch/arm64/stand/efiboot/efiboot.c | 21 +- sys/arch/arm64/stand/efiboot/efiboot.h | 3 +- sys/arch/arm64/stand/efiboot/eficall.h | 10 +- sys/arch/arm64/stand/efiboot/efipxe.c | 293 +++++++++++++++++++++++++ sys/arch/arm64/stand/efiboot/efipxe.h | 21 ++ 7 files changed, 345 insertions(+), 17 deletions(-) create mode 100644 sys/arch/arm64/stand/efiboot/efipxe.c create mode 100644 sys/arch/arm64/stand/efiboot/efipxe.h diff --git a/sys/arch/arm64/stand/efiboot/Makefile b/sys/arch/arm64/stand/efiboot/Makefile index 324528d3c8e..256d42c22ad 100644 --- a/sys/arch/arm64/stand/efiboot/Makefile +++ b/sys/arch/arm64/stand/efiboot/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.2 2017/07/17 13:46:53 kettenis Exp $ +# $OpenBSD: Makefile,v 1.3 2018/01/21 21:35:34 patrick Exp $ NOMAN= # @@ -8,7 +8,8 @@ PROG= BOOTAA64.EFI OBJFMT= binary INSTALL_STRIP= BINDIR= /usr/mdec -SRCS= start.S self_reloc.c efiboot.c conf.c exec.c efidev.c fdt.c +SRCS= start.S self_reloc.c efiboot.c conf.c exec.c efidev.c efipxe.c +SRCS+= fdt.c S= ${.CURDIR}/../../../.. EFIDIR= ${S}/stand/efi diff --git a/sys/arch/arm64/stand/efiboot/conf.c b/sys/arch/arm64/stand/efiboot/conf.c index 0fa040972e6..e504d624820 100644 --- a/sys/arch/arm64/stand/efiboot/conf.c +++ b/sys/arch/arm64/stand/efiboot/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.9 2017/09/08 05:36:51 deraadt Exp $ */ +/* $OpenBSD: conf.c,v 1.10 2018/01/21 21:35:34 patrick Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff @@ -28,22 +28,27 @@ #include #include +#include #include #include #include "efiboot.h" #include "efidev.h" +#include "efipxe.h" -const char version[] = "0.8"; +const char version[] = "0.9"; int debug = 0; 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 }, }; int nfsys = nitems(file_system); struct devsw devsw[] = { + { "tftp", tftpstrategy, tftpopen, tftpclose, tftpioctl }, { "sd", efistrategy, efiopen, eficlose, efiioctl }, }; int ndevs = nitems(devsw); diff --git a/sys/arch/arm64/stand/efiboot/efiboot.c b/sys/arch/arm64/stand/efiboot/efiboot.c index a416ba02a76..c8654196bcf 100644 --- a/sys/arch/arm64/stand/efiboot/efiboot.c +++ b/sys/arch/arm64/stand/efiboot/efiboot.c @@ -1,4 +1,4 @@ -/* $OpenBSD: efiboot.c,v 1.13 2017/08/23 18:03:54 kettenis Exp $ */ +/* $OpenBSD: efiboot.c,v 1.14 2018/01/21 21:35:34 patrick Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko @@ -31,6 +31,7 @@ #include #include "disk.h" +#include "efiboot.h" #include "eficall.h" #include "fdt.h" #include "libsa.h" @@ -53,8 +54,8 @@ static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; -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_timer_init(void); @@ -188,7 +189,7 @@ efi_diskprobe(void) 0, &sz, handles); } if (handles == NULL || EFI_ERROR(status)) - panic("BS->LocateHandle() returns %d", status); + return; if (efi_bootdp != NULL) depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH); @@ -231,7 +232,7 @@ efi_diskprobe(void) * 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; @@ -244,7 +245,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; @@ -457,6 +458,7 @@ machdep(void) efi_timer_init(); efi_diskprobe(); + efi_pxeprobe(); } void @@ -540,7 +542,10 @@ getsecs(void) void devboot(dev_t dev, char *p) { - strlcpy(p, "sd0a", 5); + if (disk) + strlcpy(p, "sd0a", 5); + else + strlcpy(p, "tftp0a", 7); } int @@ -641,7 +646,7 @@ devopen(struct open_file *f, const char *fname, char **file) if (error) return (error); - dp = &devsw[0]; + dp = &devsw[dev]; f->f_dev = dp; return (*dp->dv_open)(f, unit, part); diff --git a/sys/arch/arm64/stand/efiboot/efiboot.h b/sys/arch/arm64/stand/efiboot/efiboot.h index 87d92558d10..8f69bb715a1 100644 --- a/sys/arch/arm64/stand/efiboot/efiboot.h +++ b/sys/arch/arm64/stand/efiboot/efiboot.h @@ -1,4 +1,4 @@ -/* $OpenBSD: efiboot.h,v 1.1 2016/12/17 23:38:33 patrick Exp $ */ +/* $OpenBSD: efiboot.h,v 1.2 2018/01/21 21:35:34 patrick Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko @@ -18,6 +18,7 @@ void efi_cleanup(void); void efi_diskprobe(void); +void efi_pxeprobe(void); void *efi_makebootargs(char *); void efi_cons_probe(struct consdev *); void efi_cons_init(struct consdev *); diff --git a/sys/arch/arm64/stand/efiboot/eficall.h b/sys/arch/arm64/stand/efiboot/eficall.h index 6f4c2168acc..dbf2e718a88 100644 --- a/sys/arch/arm64/stand/efiboot/eficall.h +++ b/sys/arch/arm64/stand/efiboot/eficall.h @@ -1,4 +1,4 @@ -/* $OpenBSD: eficall.h,v 1.2 2016/12/30 09:43:18 yasuoka Exp $ */ +/* $OpenBSD: eficall.h,v 1.3 2018/01/21 21:35:34 patrick Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko @@ -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/arm64/stand/efiboot/efipxe.c b/sys/arch/arm64/stand/efiboot/efipxe.c new file mode 100644 index 00000000000..d0684ea7c33 --- /dev/null +++ b/sys/arch/arm64/stand/efiboot/efipxe.c @@ -0,0 +1,293 @@ +/* $OpenBSD: efipxe.c,v 1.1 2018/01/21 21:35:34 patrick Exp $ */ +/* + * Copyright (c) 2017 Patrick Wildt + * + * 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 +#include + +#include +#include + +#include +#include +#include "eficall.h" +#include "efiboot.h" +#include "disk.h" + +extern EFI_BOOT_SERVICES *BS; +extern EFI_DEVICE_PATH *efi_bootdp; + +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; + +extern int efi_device_path_depth(EFI_DEVICE_PATH *dp, int); +extern 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)); + 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, ...) +{ + u_int unit, part; + va_list ap; + + va_start(ap, f); + unit = va_arg(ap, u_int); + part = va_arg(ap, u_int); + va_end(ap); + + /* No PXE set -> no PXE available */ + if (PXE == NULL) + return 1; + + if (unit != 0) + return 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/arm64/stand/efiboot/efipxe.h b/sys/arch/arm64/stand/efiboot/efipxe.h new file mode 100644 index 00000000000..3ff18e0d601 --- /dev/null +++ b/sys/arch/arm64/stand/efiboot/efipxe.h @@ -0,0 +1,21 @@ +/* $OpenBSD: efipxe.h,v 1.1 2018/01/21 21:35:34 patrick Exp $ */ +/* + * Copyright (c) 2017 Patrick Wildt + * + * 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 *); -- 2.20.1