Index: head/sys/boot/efi/loader/main.c =================================================================== --- head/sys/boot/efi/loader/main.c +++ head/sys/boot/efi/loader/main.c @@ -795,6 +795,100 @@ 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) Index: head/sys/boot/forth/menu.rc =================================================================== --- head/sys/boot/forth/menu.rc +++ head/sys/boot/forth/menu.rc @@ -73,7 +73,22 @@ 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 Index: head/sys/boot/i386/libi386/Makefile =================================================================== --- head/sys/boot/i386/libi386/Makefile +++ head/sys/boot/i386/libi386/Makefile @@ -6,7 +6,7 @@ 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 Index: head/sys/boot/i386/libi386/libi386.h =================================================================== --- head/sys/boot/i386/libi386/libi386.h +++ head/sys/boot/i386/libi386/libi386.h @@ -60,6 +60,35 @@ } 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); Index: head/sys/boot/i386/libi386/relocater_tramp.S =================================================================== --- head/sys/boot/i386/libi386/relocater_tramp.S +++ head/sys/boot/i386/libi386/relocater_tramp.S @@ -0,0 +1,358 @@ +/*- + * Copyright 2015 Toomas Soome + * 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 Index: head/sys/boot/i386/loader/Makefile =================================================================== --- head/sys/boot/i386/loader/Makefile +++ head/sys/boot/i386/loader/Makefile @@ -14,7 +14,7 @@ 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) Index: head/sys/boot/i386/loader/chain.c =================================================================== --- head/sys/boot/i386/loader/chain.c +++ head/sys/boot/i386/loader/chain.c @@ -0,0 +1,135 @@ +/*- + * Copyright 2015 Toomas Soome + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#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 */ +} Index: head/sys/boot/i386/loader/help.i386 =================================================================== --- head/sys/boot/i386/loader/help.i386 +++ head/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. + +################################################################################