summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorPatrick Wildt <patrick@cvs.openbsd.org>2017-11-25 19:02:08 +0000
committerPatrick Wildt <patrick@cvs.openbsd.org>2017-11-25 19:02:08 +0000
commitc17f116572bef7f1b2ef36c47da2dd5b19c2ee2c (patch)
treeb0e6719c3b8969c5f3abafa17d80444e26886878 /sys
parent8dc408dc9c5ed5904f73b4f3c8e43cbcb3bd21fc (diff)
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 as needed on ARM. Feedback and ok tom@ Also tested by afresh1@
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/amd64/stand/efiboot/Makefile.common4
-rw-r--r--sys/arch/amd64/stand/efiboot/conf.c14
-rw-r--r--sys/arch/amd64/stand/efiboot/efiboot.c16
-rw-r--r--sys/arch/amd64/stand/efiboot/efiboot.h3
-rw-r--r--sys/arch/amd64/stand/efiboot/eficall.h10
-rw-r--r--sys/arch/amd64/stand/efiboot/efipxe.c301
-rw-r--r--sys/arch/amd64/stand/efiboot/efipxe.h20
-rw-r--r--sys/arch/amd64/stand/libsa/dev_i386.c13
8 files changed, 361 insertions, 20 deletions
diff --git a/sys/arch/amd64/stand/efiboot/Makefile.common b/sys/arch/amd64/stand/efiboot/Makefile.common
index 6dc1cdebd4d..25c7a8f1e65 100644
--- a/sys/arch/amd64/stand/efiboot/Makefile.common
+++ b/sys/arch/amd64/stand/efiboot/Makefile.common
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile.common,v 1.11 2017/06/19 22:50:50 deraadt Exp $
+# $OpenBSD: Makefile.common,v 1.12 2017/11/25 19:02:07 patrick Exp $
S= ${.CURDIR}/../../../../..
SADIR= ${.CURDIR}/../..
@@ -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 9d825543ec0..fae68c716b5 100644
--- a/sys/arch/amd64/stand/efiboot/conf.c
+++ b/sys/arch/amd64/stand/efiboot/conf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: conf.c,v 1.11 2017/09/08 05:36:51 deraadt Exp $ */
+/* $OpenBSD: conf.c,v 1.12 2017/11/25 19:02:07 patrick Exp $ */
/*
* Copyright (c) 1996 Michael Shalayeff
@@ -30,14 +30,16 @@
#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.35";
+const char version[] = "3.36";
#ifdef EFI_DEBUG
int debug = 0;
@@ -50,7 +52,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[] = {
@@ -61,6 +63,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,
@@ -75,10 +79,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 90bd85576fd..4e2f63aa9fc 100644
--- a/sys/arch/amd64/stand/efiboot/efiboot.c
+++ b/sys/arch/amd64/stand/efiboot/efiboot.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: efiboot.c,v 1.27 2017/11/06 09:08:53 yasuoka Exp $ */
+/* $OpenBSD: efiboot.c,v 1.28 2017/11/25 19:02:07 patrick Exp $ */
/*
* Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
@@ -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);
@@ -101,6 +100,11 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
? 0x1e0 : 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;
}
}
}
@@ -233,7 +237,7 @@ next:
* 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;
@@ -246,7 +250,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 09ba95ebe9d..5f5052587fa 100644
--- a/sys/arch/amd64/stand/efiboot/efiboot.h
+++ b/sys/arch/amd64/stand/efiboot/efiboot.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: efiboot.h,v 1.3 2017/10/11 04:07:50 yasuoka Exp $ */
+/* $OpenBSD: efiboot.h,v 1.4 2017/11/25 19:02:07 patrick Exp $ */
/*
* Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
@@ -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..0e6276414ce 100644
--- a/sys/arch/amd64/stand/efiboot/eficall.h
+++ b/sys/arch/amd64/stand/efiboot/eficall.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: eficall.h,v 1.2 2016/12/30 08:26:09 yasuoka Exp $ */
+/* $OpenBSD: eficall.h,v 1.3 2017/11/25 19:02:07 patrick Exp $ */
/*
* Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
@@ -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..8a6c7851196
--- /dev/null
+++ b/sys/arch/amd64/stand/efiboot/efipxe.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2017 Patrick Wildt <patrick@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;
+
+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));
+ 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 <patrick@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..308da2d08f6 100644
--- a/sys/arch/amd64/stand/libsa/dev_i386.c
+++ b/sys/arch/amd64/stand/libsa/dev_i386.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dev_i386.c,v 1.20 2017/05/31 08:40:32 yasuoka Exp $ */
+/* $OpenBSD: dev_i386.c,v 1.21 2017/11/25 19:02:07 patrick Exp $ */
/*
* Copyright (c) 1996-1999 Michael Shalayeff
@@ -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