aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorToomas Soome <tsoome@FreeBSD.org>2017-06-16 20:08:44 +0000
committerToomas Soome <tsoome@FreeBSD.org>2017-06-16 20:08:44 +0000
commit769bad9f8a55c05abac59cbf35df663a79eb7436 (patch)
tree4fc254d1768e7f9f77522eeceaa9f2741237cfb0 /sys
parenta4110f9ffabd212bcf998eed0703389a3252ec11 (diff)
downloadsrc-769bad9f8a55c05abac59cbf35df663a79eb7436.tar.gz
src-769bad9f8a55c05abac59cbf35df663a79eb7436.zip
Add chain loader support for loader
Implement simple chain loader in loader; this update does add chain command, taking device or file as argument to load and start new boot loader. In case of BIOS, the chain will read the boot block to address 0000:7c00 and jumps on it. In case of UEFI, the chain command is to be used with efi application, typically stored in EFI System Partition. The update also does add simple menu entry, if the variable chain_disk is set. The value of the variable chain_disk is used as argument for chain loading. Relnotes: yes Differential Revision: https://reviews.freebsd.org/D5992
Notes
Notes: svn path=/head/; revision=320011
Diffstat (limited to 'sys')
-rw-r--r--sys/boot/efi/loader/main.c94
-rw-r--r--sys/boot/forth/menu.rc15
-rw-r--r--sys/boot/i386/libi386/Makefile2
-rw-r--r--sys/boot/i386/libi386/libi386.h29
-rw-r--r--sys/boot/i386/libi386/relocater_tramp.S358
-rw-r--r--sys/boot/i386/loader/Makefile2
-rw-r--r--sys/boot/i386/loader/chain.c135
-rw-r--r--sys/boot/i386/loader/help.i3869
8 files changed, 642 insertions, 2 deletions
diff --git a/sys/boot/efi/loader/main.c b/sys/boot/efi/loader/main.c
index 043306f69139..6fab757be5f1 100644
--- a/sys/boot/efi/loader/main.c
+++ b/sys/boot/efi/loader/main.c
@@ -795,6 +795,100 @@ command_fdt(int argc, char *argv[])
COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
#endif
+/*
+ * Chain load another efi loader.
+ */
+static int
+command_chain(int argc, char *argv[])
+{
+ EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
+ EFI_HANDLE loaderhandle;
+ EFI_LOADED_IMAGE *loaded_image;
+ EFI_STATUS status;
+ struct stat st;
+ struct devdesc *dev;
+ char *name, *path;
+ void *buf;
+ int fd;
+
+ if (argc < 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ name = argv[1];
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ command_errmsg = "no such file";
+ return (CMD_ERROR);
+ }
+
+ if (fstat(fd, &st) < -1) {
+ command_errmsg = "stat failed";
+ close(fd);
+ return (CMD_ERROR);
+ }
+
+ status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "failed to allocate buffer";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ if (read(fd, buf, st.st_size) != st.st_size) {
+ command_errmsg = "error while reading the file";
+ (void)BS->FreePool(buf);
+ close(fd);
+ return (CMD_ERROR);
+ }
+ close(fd);
+ status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle);
+ (void)BS->FreePool(buf);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "LoadImage failed";
+ return (CMD_ERROR);
+ }
+ status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID,
+ (void **)&loaded_image);
+
+ if (argc > 2) {
+ int i, len = 0;
+ CHAR16 *argp;
+
+ for (i = 2; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+
+ len *= sizeof (*argp);
+ loaded_image->LoadOptions = argp = malloc (len);
+ loaded_image->LoadOptionsSize = len;
+ for (i = 2; i < argc; i++) {
+ char *ptr = argv[i];
+ while (*ptr)
+ *(argp++) = *(ptr++);
+ *(argp++) = ' ';
+ }
+ *(--argv) = 0;
+ }
+
+ if (efi_getdev((void **)&dev, name, (const char **)&path) == 0)
+ loaded_image->DeviceHandle =
+ efi_find_handle(dev->d_dev, dev->d_unit);
+
+ dev_cleanup();
+ status = BS->StartImage(loaderhandle, NULL, NULL);
+ if (status != EFI_SUCCESS) {
+ command_errmsg = "StartImage failed";
+ free(loaded_image->LoadOptions);
+ loaded_image->LoadOptions = NULL;
+ status = BS->UnloadImage(loaded_image);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_ERROR); /* not reached */
+}
+
+COMMAND_SET(chain, "chain", "chain load file", command_chain);
+
#ifdef EFI_ZFS_BOOT
static void
efi_zfs_probe(void)
diff --git a/sys/boot/forth/menu.rc b/sys/boot/forth/menu.rc
index 3c7de7138b8a..6fe3dfe14281 100644
--- a/sys/boot/forth/menu.rc
+++ b/sys/boot/forth/menu.rc
@@ -73,8 +73,23 @@ s" currdev" getenv dup 0> [if] drop 4 s" zfs:" compare 0= [if]
set mainmenu_command[7]="3 goto_menu"
set mainmenu_keycode[7]=101
set mainansi_caption[7]="Select Boot ^[1mE^[37mnvironment..."
+
+ s" chain_disk" getenv? [if]
+ set mainmenu_caption[8]="Chain[L]oad ${chain_disk}"
+ set mainmenu_command[8]="chain ${chain_disk}"
+ set mainmenu_keycode[8]=108
+ set mainansi_caption[8]="Chain^[1mL^[moad ${chain_disk}"
+ [then]
+[else]
+ s" chain_disk" getenv? [if]
+ set mainmenu_caption[7]="Chain[L]oad ${chain_disk}"
+ set mainmenu_command[7]="chain ${chain_disk}"
+ set mainmenu_keycode[7]=108
+ set mainansi_caption[7]="Chain^[1mL^[moad ${chain_disk}"
+ [then]
[then] [else] drop [then]
+
\
\ BOOT OPTIONS MENU
\
diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile
index 88c1c07ab486..2a7d1e2213df 100644
--- a/sys/boot/i386/libi386/Makefile
+++ b/sys/boot/i386/libi386/Makefile
@@ -6,7 +6,7 @@ INTERNALLIB=
SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \
biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \
comconsole.c devicename.c elf32_freebsd.c \
- elf64_freebsd.c multiboot.c multiboot_tramp.S \
+ elf64_freebsd.c multiboot.c multiboot_tramp.S relocater_tramp.S \
i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \
smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c
.PATH: ${.CURDIR}/../../zfs
diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h
index 76f02718ccdb..8cc7452270a7 100644
--- a/sys/boot/i386/libi386/libi386.h
+++ b/sys/boot/i386/libi386/libi386.h
@@ -60,6 +60,35 @@ struct i386_devdesc
} d_kind;
};
+/*
+ * relocater trampoline support.
+ */
+struct relocate_data {
+ uint32_t src;
+ uint32_t dest;
+ uint32_t size;
+};
+
+extern void relocater(void);
+
+extern uint32_t relocater_data;
+extern uint32_t relocater_size;
+
+extern uint16_t relocator_ip;
+extern uint16_t relocator_cs;
+extern uint16_t relocator_ds;
+extern uint16_t relocator_es;
+extern uint16_t relocator_fs;
+extern uint16_t relocator_gs;
+extern uint16_t relocator_ss;
+extern uint16_t relocator_sp;
+extern uint32_t relocator_esi;
+extern uint32_t relocator_eax;
+extern uint32_t relocator_ebx;
+extern uint32_t relocator_edx;
+extern uint32_t relocator_ebp;
+extern uint16_t relocator_a20_enabled;
+
int i386_getdev(void **vdev, const char *devspec, const char **path);
char *i386_fmtdev(void *vdev);
int i386_setcurrdev(struct env_var *ev, int flags, const void *value);
diff --git a/sys/boot/i386/libi386/relocater_tramp.S b/sys/boot/i386/libi386/relocater_tramp.S
new file mode 100644
index 000000000000..6db4a7509b2c
--- /dev/null
+++ b/sys/boot/i386/libi386/relocater_tramp.S
@@ -0,0 +1,358 @@
+/*-
+ * Copyright 2015 Toomas Soome <tsoome@me.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * relocate is needed to support loading code which has to be located
+ * below 1MB, as both BTX and loader are using low memory area.
+ *
+ * relocate and start loaded code. Since loaded code may need to be
+ * placed to already occupied memory area, this code is moved to safe
+ * memory area and then btx __exec will be called with physical pointer
+ * to this area. __exec will set pointer to %eax and use call *%eax,
+ * so on entry, we have new "base" address in %eax.
+ *
+ * Relocate will first set up and load new safe GDT to shut down BTX,
+ * then loaded code will be relocated to final memory location,
+ * then machine will be switched from 32bit protected mode to 16bit
+ * protected mode following by switch to real mode with A20 enabled or
+ * disabled. Finally the loaded code will be started and it will take
+ * over the whole system.
+ *
+ * For now, the known "safe" memory area for relocate is 0x600,
+ * the actual "free" memory is supposed to start from 0x500, leaving
+ * first 0x100 bytes in reserve. As relocate code+data is very small,
+ * it will leave enough space to set up boot blocks to 0:7c00 or load
+ * linux kernel below 1MB space.
+ */
+/*
+ * segment selectors
+ */
+ .set SEL_SCODE,0x8
+ .set SEL_SDATA,0x10
+ .set SEL_RCODE,0x18
+ .set SEL_RDATA,0x20
+
+ .p2align 4
+ .globl relocater
+relocater:
+ cli
+ /*
+ * set up GDT from new location
+ */
+ movl %eax, %esi /* our base address */
+ add $(relocater.1-relocater), %eax
+ jmp *%eax
+relocater.1:
+ /* set up jump */
+ lea (relocater.2-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+
+ /* set up gdt */
+ lea (gdt-relocater) (%esi), %eax
+ movl %eax, (gdtaddr-relocater) (%esi)
+
+ /* load gdt */
+ lgdt (gdtdesc - relocater) (%esi)
+ lidt (idt-relocater) (%esi)
+
+ /* update cs */
+ ljmp *(jump_vector-relocater) (%esi)
+
+ .code32
+relocater.2:
+ xorl %eax, %eax
+ movb $SEL_SDATA, %al
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movl %cr0, %eax /* disable paging */
+ andl $~0x80000000,%eax
+ movl %eax, %cr0
+ xorl %ecx, %ecx /* flush TLB */
+ movl %ecx, %cr3
+ cld
+/*
+ * relocate data loop. load source, dest and size from
+ * relocater_data[i], 0 value will stop the loop.
+ * registers used for move: %esi, %edi, %ecx.
+ * %ebx to keep base
+ * %edx for relocater_data offset
+ */
+ movl %esi, %ebx /* base address */
+ xorl %edx, %edx
+loop.1:
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %eax
+ testl %eax, %eax
+ jz loop.2
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %esi
+ inc %edx
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %edi
+ inc %edx
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %ecx
+ inc %edx
+ rep
+ movsb
+ jmp loop.1
+loop.2:
+ movl %ebx, %esi /* restore esi */
+ /*
+ * data is relocated, switch to 16bit mode
+ */
+ lea (relocater.3-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+ movl $SEL_RCODE, %eax
+ movl %eax, (jump_vector-relocater+4) (%esi)
+
+ ljmp *(jump_vector-relocater) (%esi)
+relocater.3:
+ .code16
+
+ movw $SEL_RDATA, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ lidt (idt-relocater) (%esi)
+ lea (relocater.4-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+ xorl %eax, %eax
+ movl %eax, (jump_vector-relocater+4) (%esi)
+ /* clear PE */
+ movl %cr0, %eax
+ dec %al
+ movl %eax, %cr0
+ ljmp *(jump_vector-relocater) (%esi)
+relocater.4:
+ xorw %ax, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ /*
+ * set real mode irq offsets
+ */
+ movw $0x7008,%bx
+ in $0x21,%al # Save master
+ push %ax # IMR
+ in $0xa1,%al # Save slave
+ push %ax # IMR
+ movb $0x11,%al # ICW1 to
+ outb %al,$0x20 # master,
+ outb %al,$0xa0 # slave
+ movb %bl,%al # ICW2 to
+ outb %al,$0x21 # master
+ movb %bh,%al # ICW2 to
+ outb %al,$0xa1 # slave
+ movb $0x4,%al # ICW3 to
+ outb %al,$0x21 # master
+ movb $0x2,%al # ICW3 to
+ outb %al,$0xa1 # slave
+ movb $0x1,%al # ICW4 to
+ outb %al,$0x21 # master,
+ outb %al,$0xa1 # slave
+ pop %ax # Restore slave
+ outb %al,$0xa1 # IMR
+ pop %ax # Restore master
+ outb %al,$0x21 # IMR
+ # done
+ /*
+ * Should A20 be left enabled?
+ */
+ /* movw imm16, %ax */
+ .byte 0xb8
+ .globl relocator_a20_enabled
+relocator_a20_enabled:
+ .word 0
+ test %ax, %ax
+ jnz a20_done
+
+ movw $0xa00, %ax
+ movw %ax, %sp
+ movw %ax, %bp
+
+ /* Disable A20 */
+ movw $0x2400, %ax
+ int $0x15
+# jnc a20_done
+
+ call a20_check_state
+ testb %al, %al
+ jz a20_done
+
+ inb $0x92
+ andb $(~0x03), %al
+ outb $0x92
+ jmp a20_done
+
+a20_check_state:
+ movw $100, %cx
+1:
+ xorw %ax, %ax
+ movw %ax, %ds
+ decw %ax
+ movw %ax, %es
+ xorw %ax, %ax
+ movw $0x8000, %ax
+ movw %ax, %si
+ addw $0x10, %ax
+ movw %ax, %di
+ movb %ds:(%si), %dl
+ movb %es:(%di), %al
+ movb %al, %dh
+ decb %dh
+ movb %dh, %ds:(%si)
+ outb %al, $0x80
+ outb %al, $0x80
+ movb %es:(%di), %dh
+ subb %dh, %al
+ xorb $1, %al
+ movb %dl, %ds:(%si)
+ testb %al, %al
+ jz a20_done
+ loop 1b
+ ret
+a20_done:
+ /*
+ * set up registers
+ */
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_ds
+relocator_ds: .word 0
+ movw %ax, %ds
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_es
+relocator_es: .word 0
+ movw %ax, %es
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_fs
+relocator_fs: .word 0
+ movw %ax, %fs
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_gs
+relocator_gs: .word 0
+ movw %ax, %gs
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_ss
+relocator_ss: .word 0
+ movw %ax, %ss
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_sp
+relocator_sp: .word 0
+ movzwl %ax, %esp
+
+ /* movw imm32, %eax. */
+ .byte 0x66, 0xb8
+ .globl relocator_esi
+relocator_esi: .long 0
+ movl %eax, %esi
+
+ /* movw imm32, %edx. */
+ .byte 0x66, 0xba
+ .globl relocator_edx
+relocator_edx: .long 0
+
+ /* movw imm32, %ebx. */
+ .byte 0x66, 0xbb
+ .globl relocator_ebx
+relocator_ebx: .long 0
+
+ /* movw imm32, %eax. */
+ .byte 0x66, 0xb8
+ .globl relocator_eax
+relocator_eax: .long 0
+
+ /* movw imm32, %ebp. */
+ .byte 0x66, 0xbd
+ .globl relocator_ebp
+relocator_ebp: .long 0
+
+ sti
+ .byte 0xea /* ljmp */
+ .globl relocator_ip
+relocator_ip:
+ .word 0
+ .globl relocator_cs
+relocator_cs:
+ .word 0
+
+/* GDT to reset BTX */
+ .code32
+ .p2align 4
+jump_vector: .long 0
+ .long SEL_SCODE
+
+gdt: .word 0x0, 0x0 /* null entry */
+ .byte 0x0, 0x0, 0x0, 0x0
+ .word 0xffff, 0x0 /* SEL_SCODE */
+ .byte 0x0, 0x9a, 0xcf, 0x0
+ .word 0xffff, 0x0 /* SEL_SDATA */
+ .byte 0x0, 0x92, 0xcf, 0x0
+ .word 0xffff, 0x0 /* SEL_RCODE */
+ .byte 0x0, 0x9a, 0x0f, 0x0
+ .word 0xffff, 0x0 /* SEL_RDATA */
+ .byte 0x0, 0x92, 0x0f, 0x0
+gdt.1:
+
+gdtdesc: .word gdt.1 - gdt - 1 /* limit */
+gdtaddr: .long 0 /* base */
+
+idt: .word 0x3ff
+ .long 0
+
+ .globl relocater_data
+relocater_data:
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0
+
+ .globl relocater_size
+relocater_size:
+ .long relocater_size-relocater
diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile
index 84404efb05d3..434ccc914946 100644
--- a/sys/boot/i386/loader/Makefile
+++ b/sys/boot/i386/loader/Makefile
@@ -14,7 +14,7 @@ LOADER_NFS_SUPPORT?= yes
LOADER_TFTP_SUPPORT?= yes
# architecture-specific loader code
-SRCS= main.c conf.c vers.c
+SRCS= main.c conf.c vers.c chain.c
# Put LOADER_FIREWIRE_SUPPORT=yes in /etc/make.conf for FireWire/dcons support
.if defined(LOADER_FIREWIRE_SUPPORT)
diff --git a/sys/boot/i386/loader/chain.c b/sys/boot/i386/loader/chain.c
new file mode 100644
index 000000000000..f8c83d82e9a2
--- /dev/null
+++ b/sys/boot/i386/loader/chain.c
@@ -0,0 +1,135 @@
+/*-
+ * Copyright 2015 Toomas Soome <tsoome@me.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Chain loader to load BIOS boot block either from MBR or PBR.
+ *
+ * Note the boot block location 0000:7c000 conflicts with loader, so we need to
+ * read in to temporary space and relocate on exec, when btx is stopped.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/diskmbr.h>
+
+#include "bootstrap.h"
+#include "libi386/libi386.h"
+#include "btxv86.h"
+
+/*
+ * The MBR/VBR is located in first sector of disk/partition.
+ * Read 512B to temporary location and set up relocation. Then
+ * exec relocator.
+ */
+#define SECTOR_SIZE (512)
+
+COMMAND_SET(chain, "chain", "chain load boot block from device", command_chain);
+
+static int
+command_chain(int argc, char *argv[])
+{
+ int fd, len, size = SECTOR_SIZE;
+ struct stat st;
+ vm_offset_t mem = 0x100000;
+ uint32_t *uintptr = &relocater_data;
+ struct i386_devdesc *rootdev;
+
+ if (argc == 1) {
+ command_errmsg = "no device or file name specified";
+ return (CMD_ERROR);
+ }
+ if (argc != 2) {
+ command_errmsg = "invalid trailing arguments";
+ return (CMD_ERROR);
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd == -1) {
+ command_errmsg = "open failed";
+ return (CMD_ERROR);
+ }
+
+ len = strlen(argv[1]);
+ if (argv[1][len-1] != ':') {
+ if (fstat(fd, &st) == -1) {
+ command_errmsg = "stat failed";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ size = st.st_size;
+ } else if (strncmp(argv[1], "disk", 4) != 0) {
+ command_errmsg = "can only use disk device";
+ close(fd);
+ return (CMD_ERROR);
+ }
+
+ i386_getdev((void **)(&rootdev), argv[1], NULL);
+ if (rootdev == NULL) {
+ command_errmsg = "can't determine root device";
+ return (CMD_ERROR);
+ }
+
+ if (archsw.arch_readin(fd, mem, SECTOR_SIZE) != SECTOR_SIZE) {
+ command_errmsg = "failed to read disk";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ close(fd);
+
+ if (*((uint16_t *)PTOV(mem + DOSMAGICOFFSET)) != DOSMAGIC) {
+ command_errmsg = "wrong magic";
+ return (CMD_ERROR);
+ }
+
+ uintptr[0] = mem;
+ uintptr[1] = 0x7C00;
+ uintptr[2] = SECTOR_SIZE;
+
+ relocator_edx = bd_unit2bios(rootdev->d_unit);
+ relocator_esi = relocater_size;
+ relocator_ds = 0;
+ relocator_es = 0;
+ relocator_fs = 0;
+ relocator_gs = 0;
+ relocator_ss = 0;
+ relocator_cs = 0;
+ relocator_sp = 0x7C00;
+ relocator_ip = 0x7C00;
+ relocator_a20_enabled = 0;
+
+ i386_copyin(relocater, 0x600, relocater_size);
+
+ dev_cleanup();
+
+ __exec((void *)0x600);
+
+ panic("exec returned");
+ return (CMD_ERROR); /* not reached */
+}
diff --git a/sys/boot/i386/loader/help.i386 b/sys/boot/i386/loader/help.i386
index dc285347c3a7..0ff628643c87 100644
--- a/sys/boot/i386/loader/help.i386
+++ b/sys/boot/i386/loader/help.i386
@@ -43,3 +43,12 @@
Displays the BIOS SMAP (system memory map) table.
################################################################################
+# Tchain DChain load disk block
+
+ chain disk:
+
+ chain will read stage1 (MBR or VBR) boot block from specified device
+ to address 0000:7C00 and attempts to run it. Use lsdev to get available
+ device names. Disk name must end with colon.
+
+################################################################################