diff --git a/stand/common/bootstrap.h b/stand/common/bootstrap.h index 7387569ad65a..b7d6e538f9be 100644 --- a/stand/common/bootstrap.h +++ b/stand/common/bootstrap.h @@ -1,389 +1,385 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #ifndef _BOOTSTRAP_H_ #define _BOOTSTRAP_H_ #include #include #include #include #include #include "readin.h" /* Commands and return values; nonzero return sets command_errmsg != NULL */ typedef int (bootblk_cmd_t)(int argc, char *argv[]); #define COMMAND_ERRBUFSZ (256) extern const char *command_errmsg; extern char command_errbuf[COMMAND_ERRBUFSZ]; #define CMD_OK 0 #define CMD_WARN 1 #define CMD_ERROR 2 #define CMD_CRIT 3 #define CMD_FATAL 4 /* interp.c */ void interact(void); void interp_emit_prompt(void); int interp_builtin_cmd(int argc, char *argv[]); bool interp_has_builtin_cmd(const char *cmd); /* Called by interp.c for interp_*.c embedded interpreters */ int interp_include(const char *); /* Execute commands from filename */ void interp_init(void); /* Initialize interpreater */ int interp_run(const char *); /* Run a single command */ /* interp_backslash.c */ char *backslash(const char *str); /* interp_parse.c */ int parse(int *argc, char ***argv, const char *str); /* boot.c */ void autoboot_maybe(void); int getrootmount(char *rootdev); /* misc.c */ char *unargv(int argc, char *argv[]); size_t strlenout(vm_offset_t str); char *strdupout(vm_offset_t str); void kern_bzero(vm_offset_t dest, size_t len); int kern_pread(readin_handle_t fd, vm_offset_t dest, size_t len, off_t off); void *alloc_pread(readin_handle_t fd, off_t off, size_t len); /* bcache.c */ void bcache_init(size_t nblks, size_t bsize); void bcache_add_dev(int); void *bcache_allocate(void); void bcache_free(void *); int bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); /* * Disk block cache */ struct bcache_devdata { int (*dv_strategy)(void *, int, daddr_t, size_t, char *, size_t *); void *dv_devdata; void *dv_cache; }; /* * Modular console support. */ struct console { const char *c_name; const char *c_desc; int c_flags; #define C_PRESENTIN (1<<0) /* console can provide input */ #define C_PRESENTOUT (1<<1) /* console can provide output */ #define C_ACTIVEIN (1<<2) /* user wants input from console */ #define C_ACTIVEOUT (1<<3) /* user wants output to console */ #define C_WIDEOUT (1<<4) /* c_out routine groks wide chars */ /* set c_flags to match hardware */ void (* c_probe)(struct console *cp); /* reinit XXX may need more args */ int (* c_init)(int arg); /* emit c */ void (* c_out)(int c); /* wait for and return input */ int (* c_in)(void); /* return nonzero if input waiting */ int (* c_ready)(void); }; extern struct console *consoles[]; void cons_probe(void); bool cons_update_mode(bool); void autoload_font(bool); extern int module_verbose; enum { MODULE_VERBOSE_SILENT, /* say nothing */ MODULE_VERBOSE_SIZE, /* print name and size */ MODULE_VERBOSE_TWIDDLE, /* show progress */ MODULE_VERBOSE_FULL, /* all we have */ }; /* * Plug-and-play enumerator/configurator interface. */ struct pnphandler { const char *pp_name; /* handler/bus name */ void (*pp_enumerate)(void); /* enumerate PnP devices, add to chain */ }; struct pnpident { /* ASCII identifier, actual format varies with bus/handler */ char *id_ident; STAILQ_ENTRY(pnpident) id_link; }; struct pnpinfo { char *pi_desc; /* ASCII description, optional */ int pi_revision; /* optional revision (or -1) if not supported */ char *pi_module; /* module/args nominated to handle device */ int pi_argc; /* module arguments */ char **pi_argv; struct pnphandler *pi_handler; /* handler which detected this device */ STAILQ_HEAD(, pnpident) pi_ident; /* list of identifiers */ STAILQ_ENTRY(pnpinfo) pi_link; }; STAILQ_HEAD(pnpinfo_stql, pnpinfo); extern struct pnphandler *pnphandlers[]; /* provided by MD code */ void pnp_addident(struct pnpinfo *pi, char *ident); struct pnpinfo *pnp_allocinfo(void); void pnp_freeinfo(struct pnpinfo *pi); void pnp_addinfo(struct pnpinfo *pi); char *pnp_eisaformat(uint8_t *data); /* * < 0 - No ISA in system * == 0 - Maybe ISA, search for read data port * > 0 - ISA in system, value is read data port address */ extern int isapnp_readport; /* * Version information */ extern char bootprog_info[]; extern unsigned bootprog_rev; /* * Interpreter information */ extern const char bootprog_interp[]; #define INTERP_DEFINE(interpstr) \ const char bootprog_interp[] = "$Interpreter:" interpstr /* * Preloaded file metadata header. * * Metadata are allocated on our heap, and copied into kernel space * before executing the kernel. */ struct file_metadata { size_t md_size; uint16_t md_type; vm_offset_t md_addr; /* Valid after copied to kernel space */ struct file_metadata *md_next; char md_data[1]; /* data are immediately appended */ }; struct preloaded_file; struct mod_depend; struct kernel_module { char *m_name; /* module name */ int m_version; /* module version */ /* char *m_args; */ /* arguments for the module */ struct preloaded_file *m_fp; struct kernel_module *m_next; }; /* * Preloaded file information. Depending on type, file can contain * additional units called 'modules'. * * At least one file (the kernel) must be loaded in order to boot. * The kernel is always loaded first. * * String fields (m_name, m_type) should be dynamically allocated. */ struct preloaded_file { char *f_name; /* file name */ char *f_type; /* verbose file type, eg 'ELF kernel', 'pnptable', etc. */ char *f_args; /* arguments for the file */ /* metadata that will be placed in the module directory */ struct file_metadata *f_metadata; int f_loader; /* index of the loader that read the file */ vm_offset_t f_addr; /* load address */ size_t f_size; /* file size */ struct kernel_module *f_modules; /* list of modules if any */ struct preloaded_file *f_next; /* next file */ #ifdef __amd64__ bool f_kernphys_relocatable; #endif #if defined(__i386__) bool f_tg_kernel_support; #endif }; struct file_format { /* * Load function must return EFTYPE if it can't handle * the module supplied */ int (*l_load)(char *, uint64_t, struct preloaded_file **); /* * Only a loader that will load a kernel (first module) * should have an exec handler */ int (*l_exec)(struct preloaded_file *); }; extern struct file_format *file_formats[]; /* supplied by consumer */ extern struct preloaded_file *preloaded_files; int mod_load(char *name, struct mod_depend *verinfo, int argc, char *argv[]); int mod_loadkld(const char *name, int argc, char *argv[]); void unload(void); struct preloaded_file *file_alloc(void); struct preloaded_file *file_findfile(const char *name, const char *type); struct file_metadata *file_findmetadata(struct preloaded_file *fp, int type); struct preloaded_file *file_loadraw(const char *name, char *type, int insert); void file_discard(struct preloaded_file *fp); void file_addmetadata(struct preloaded_file *, int, size_t, void *); int file_addmodule(struct preloaded_file *, char *, int, struct kernel_module **); void file_removemetadata(struct preloaded_file *fp); int file_addbuf(const char *name, const char *type, size_t len, void *buf); int tslog_init(void); int tslog_publish(void); vm_offset_t build_font_module(vm_offset_t); /* MI module loaders */ #ifdef __elfN /* Relocation types. */ #define ELF_RELOC_REL 1 #define ELF_RELOC_RELA 2 /* Relocation offset for some architectures */ extern uint64_t __elfN(relocation_offset); struct elf_file; typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx); int __elfN(loadfile)(char *, uint64_t, struct preloaded_file **); int __elfN(obj_loadfile)(char *, uint64_t, struct preloaded_file **); int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata, int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len); int __elfN(loadfile_raw)(char *, uint64_t, struct preloaded_file **, int); int __elfN(load_modmetadata)(struct preloaded_file *, uint64_t); #endif /* * Support for commands */ struct bootblk_command { const char *c_name; const char *c_desc; bootblk_cmd_t *c_fn; }; #define COMMAND_SET(tag, key, desc, func) \ static bootblk_cmd_t func; \ static struct bootblk_command _cmd_ ## tag = { key, desc, func }; \ DATA_SET(Xcommand_set, _cmd_ ## tag) SET_DECLARE(Xcommand_set, struct bootblk_command); /* * The intention of the architecture switch is to provide a convenient * encapsulation of the interface between the bootstrap MI and MD code. * MD code may selectively populate the switch at runtime based on the * actual configuration of the target system. */ struct arch_switch { /* Automatically load modules as required by detected hardware */ int (*arch_autoload)(void); /* Locate the device for (name), return pointer to tail in (*path) */ int (*arch_getdev)(void **dev, const char *name, const char **path); /* * Copy from local address space to module address space, * similar to bcopy() */ ssize_t (*arch_copyin)(const void *, vm_offset_t, const size_t); /* * Copy to local address space from module address space, * similar to bcopy() */ ssize_t (*arch_copyout)(const vm_offset_t, void *, const size_t); /* Read from file to module address space, same semantics as read() */ ssize_t (*arch_readin)(readin_handle_t, vm_offset_t, const size_t); /* Perform ISA byte port I/O (only for systems with ISA) */ int (*arch_isainb)(int port); void (*arch_isaoutb)(int port, int value); /* * Interface to adjust the load address according to the "object" * being loaded. */ uint64_t (*arch_loadaddr)(u_int type, void *data, uint64_t addr); #define LOAD_ELF 1 /* data points to the ELF header. */ #define LOAD_RAW 2 /* data points to the file name. */ /* * Interface to inform MD code about a loaded (ELF) segment. This * can be used to flush caches and/or set up translations. */ #ifdef __elfN void (*arch_loadseg)(Elf_Ehdr *eh, Elf_Phdr *ph, uint64_t delta); #else void (*arch_loadseg)(void *eh, void *ph, uint64_t delta); #endif /* Probe ZFS pool(s), if needed. */ void (*arch_zfs_probe)(void); /* Return the hypervisor name/type or NULL if not virtualized. */ const char *(*arch_hypervisor)(void); }; extern struct arch_switch archsw; /* This must be provided by the MD code, but should it be in the archsw? */ void delay(int delay); /* common code to set currdev variable. */ int gen_setcurrdev(struct env_var *ev, int flags, const void *value); int mount_currdev(struct env_var *, int, const void *); void set_currdev(const char *devname); -#ifndef CTASSERT -#define CTASSERT(x) _Static_assert(x, "compile-time assertion failed") -#endif - #endif /* !_BOOTSTRAP_H_ */ diff --git a/stand/efi/loader/arch/amd64/multiboot2.c b/stand/efi/loader/arch/amd64/multiboot2.c index 4d7b2713685d..6c96d7dc0073 100644 --- a/stand/efi/loader/arch/amd64/multiboot2.c +++ b/stand/efi/loader/arch/amd64/multiboot2.c @@ -1,567 +1,567 @@ /*- * Copyright (c) 2021 Roger Pau Monné * 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. */ /* * This multiboot2 implementation only implements a subset of the full * multiboot2 specification in order to be able to boot Xen and a * FreeBSD Dom0. Trying to use it to boot other multiboot2 compliant * kernels will most surely fail. * * The full multiboot specification can be found here: * https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html */ #include #include #include #include #include #include #define _MACHINE_ELF_WANT_32BIT #include #include #include #include #include #include #include "bootstrap.h" #include "multiboot2.h" #include "loader_efi.h" extern int elf32_loadfile_raw(char *filename, uint64_t dest, struct preloaded_file **result, int multiboot); extern int elf64_load_modmetadata(struct preloaded_file *fp, uint64_t dest); extern int elf64_obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result); extern int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp, bool exit_bs); extern void multiboot2_exec(void *entry, uint64_t multiboot_info, uint64_t stack); /* * Multiboot2 header information to pass between the loading and the exec * functions. */ struct mb2hdr { uint32_t efi64_entry; }; static int loadfile(char *filename, uint64_t dest, struct preloaded_file **result) { unsigned int i; int error, fd; void *header_search = NULL; void *multiboot = NULL; ssize_t search_size; struct multiboot_header *header; char *cmdline; struct mb2hdr hdr; bool keep_bs = false; /* * Read MULTIBOOT_SEARCH size in order to search for the * multiboot magic header. */ if (filename == NULL) return (EFTYPE); if ((fd = open(filename, O_RDONLY)) == -1) return (errno); header_search = malloc(MULTIBOOT_SEARCH); if (header_search == NULL) { error = ENOMEM; goto out; } search_size = read(fd, header_search, MULTIBOOT_SEARCH); for (i = 0; i < search_size; i += MULTIBOOT_HEADER_ALIGN) { header = header_search + i; if (header->magic == MULTIBOOT2_HEADER_MAGIC) break; } if (i >= search_size) { error = EFTYPE; goto out; } /* Valid multiboot header has been found, validate checksum */ if (header->magic + header->architecture + header->header_length + header->checksum != 0) { printf("Multiboot checksum failed, magic: %#x " "architecture: %#x header_length %#x checksum: %#x\n", header->magic, header->architecture, header->header_length, header->checksum); error = EFTYPE; goto out; } if (header->architecture != MULTIBOOT2_ARCHITECTURE_I386) { printf("Unsupported architecture: %#x\n", header->architecture); error = EFTYPE; goto out; } multiboot = malloc(header->header_length - sizeof(*header)); error = lseek(fd, i + sizeof(*header), SEEK_SET); if (error != i + sizeof(*header)) { printf("Unable to set file pointer to header location: %d\n", error); goto out; } search_size = read(fd, multiboot, header->header_length - sizeof(*header)); bzero(&hdr, sizeof(hdr)); for (i = 0; i < search_size; ) { struct multiboot_header_tag *tag; struct multiboot_header_tag_entry_address *entry; struct multiboot_header_tag_information_request *req; unsigned int j; tag = multiboot + i; switch(tag->type) { case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST: req = (void *)tag; for (j = 0; j < (tag->size - sizeof(*tag)) / sizeof(uint32_t); j++) { switch (req->requests[j]) { case MULTIBOOT_TAG_TYPE_MMAP: case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: /* Only applicable to BIOS. */ break; case MULTIBOOT_TAG_TYPE_EFI_BS: case MULTIBOOT_TAG_TYPE_EFI64: case MULTIBOOT_TAG_TYPE_EFI64_IH: /* Tags unconditionally added. */ break; default: if (req->flags & MULTIBOOT_HEADER_TAG_OPTIONAL) break; printf( "Unknown non-optional information request %u\n", req->requests[j]); error = EINVAL; goto out; } } break; case MULTIBOOT_HEADER_TAG_EFI_BS: /* Never shut down BS. */ keep_bs = true; break; case MULTIBOOT_HEADER_TAG_MODULE_ALIGN: /* We will align modules by default already. */ case MULTIBOOT_HEADER_TAG_END: break; case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64: entry = (void *)tag; hdr.efi64_entry = entry->entry_addr; break; default: if (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL) break; printf("Unknown header tag %#x not optional\n", tag->type); error = EINVAL; goto out; } i += roundup2(tag->size, MULTIBOOT_TAG_ALIGN); if (tag->type == MULTIBOOT_HEADER_TAG_END) break; } if (hdr.efi64_entry == 0) { printf("No EFI64 entry address provided\n"); error = EINVAL; goto out; } if (!keep_bs) { printf("Unable to boot MB2 with BS exited\n"); error = EINVAL; goto out; } error = elf32_loadfile_raw(filename, dest, result, 1); if (error != 0) { printf( "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", error); goto out; } file_addmetadata(*result, MODINFOMD_NOCOPY | MODINFOMD_MB2HDR, sizeof(hdr), &hdr); /* * f_addr is already aligned to PAGE_SIZE, make sure * f_size it's also aligned so when the modules are loaded * they are aligned to PAGE_SIZE. */ (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); out: if (header_search != NULL) free(header_search); if (multiboot != NULL) free(multiboot); close(fd); return (error); } static unsigned int add_string(void *buf, unsigned int type, const char *str) { struct multiboot_tag *tag; tag = buf; tag->type = type; tag->size = sizeof(*tag) + strlen(str) + 1; strcpy(buf + sizeof(*tag), str); return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN)); } static unsigned int add_efi(void *buf) { struct multiboot_tag *bs; struct multiboot_tag_efi64 *efi64; struct multiboot_tag_efi64_ih *ih; unsigned int len; len = 0; bs = buf; bs->type = MULTIBOOT_TAG_TYPE_EFI_BS; bs->size = sizeof(*bs); len += roundup2(bs->size, MULTIBOOT_TAG_ALIGN); efi64 = buf + len; efi64->type = MULTIBOOT_TAG_TYPE_EFI64; efi64->size = sizeof(*efi64); efi64->pointer = (uintptr_t)ST; len += roundup2(efi64->size, MULTIBOOT_TAG_ALIGN); ih = buf + len; ih->type = MULTIBOOT_TAG_TYPE_EFI64_IH; ih->size = sizeof(*ih); ih->pointer = (uintptr_t)IH; return (len + roundup2(ih->size, MULTIBOOT_TAG_ALIGN)); } static unsigned int add_module(void *buf, vm_offset_t start, vm_offset_t end, const char *cmdline) { struct multiboot_tag_module *mod; mod = buf; mod->type = MULTIBOOT_TAG_TYPE_MODULE; mod->size = sizeof(*mod); mod->mod_start = start; mod->mod_end = end; if (cmdline != NULL) { strcpy(buf + sizeof(*mod), cmdline); mod->size += strlen(cmdline) + 1; } return (roundup2(mod->size, MULTIBOOT_TAG_ALIGN)); } static unsigned int add_end(void *buf) { struct multiboot_tag *tag; tag = buf; tag->type = MULTIBOOT_TAG_TYPE_END; tag->size = sizeof(*tag); return (roundup2(tag->size, MULTIBOOT_TAG_ALIGN)); } static int exec(struct preloaded_file *fp) { EFI_PHYSICAL_ADDRESS addr = 0; EFI_PHYSICAL_ADDRESS stack = 0; EFI_STATUS status; void *multiboot_space; vm_offset_t modulep, kernend, kern_base, payload_base; char *cmdline = NULL; size_t len; int error; uint32_t *total_size; struct file_metadata *md; struct xen_header header; struct mb2hdr *hdr; - CTASSERT(sizeof(header) <= PAGE_SIZE); + _Static_assert(sizeof(header) <= PAGE_SIZE, "header too big"); if ((md = file_findmetadata(fp, MODINFOMD_NOCOPY | MODINFOMD_MB2HDR)) == NULL) { printf("Missing Multiboot2 EFI64 entry point\n"); return(EFTYPE); } hdr = (void *)&md->md_data; status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, EFI_SIZE_TO_PAGES(PAGE_SIZE), &addr); if (EFI_ERROR(status)) { printf("Failed to allocate pages for multiboot2 header: %lu\n", EFI_ERROR_CODE(status)); error = ENOMEM; goto error; } status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, EFI_SIZE_TO_PAGES(128 * 1024), &stack); if (EFI_ERROR(status)) { printf("Failed to allocate pages for Xen stack: %lu\n", EFI_ERROR_CODE(status)); error = ENOMEM; goto error; } /* * Scratch space to build the multiboot2 header. Reserve the start of * the space to place the header with the size, which we don't know * yet. */ multiboot_space = (void *)(uintptr_t)(addr + sizeof(uint32_t) * 2); /* * Don't pass the memory size found by the bootloader, the memory * available to Dom0 will be lower than that. */ unsetenv("smbios.memory.enabled"); /* Set the Xen command line. */ if (fp->f_args == NULL) { /* Add the Xen command line if it is set. */ cmdline = getenv("xen_cmdline"); if (cmdline != NULL) { fp->f_args = strdup(cmdline); if (fp->f_args == NULL) { error = ENOMEM; goto error; } } } if (fp->f_args != NULL) { len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; cmdline = malloc(len); if (cmdline == NULL) { error = ENOMEM; goto error; } snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); multiboot_space += add_string(multiboot_space, MULTIBOOT_TAG_TYPE_CMDLINE, cmdline); free(cmdline); } multiboot_space += add_string(multiboot_space, MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME, "FreeBSD Loader"); multiboot_space += add_efi(multiboot_space); /* * Prepare the multiboot module list, Xen assumes the first * module is the Dom0 kernel, and the second one is the initramfs. * This is not optimal for FreeBSD, that doesn't have a initramfs * but instead loads modules dynamically and creates the metadata * info on-the-fly. * * As expected, the first multiboot module is going to be the * FreeBSD kernel loaded as a raw file. The second module is going * to contain the metadata info and the loaded modules. * * There's a small header prefixed in the second module that contains * some information required to calculate the relocated address of * modulep based on the original offset of modulep from the start of * the module address. Note other fields might be added to this header * if required. * * Native layout: * fp->f_addr + fp->f_size * +---------+----------------+------------+ * | | | | * | Kernel | Modules | Metadata | * | | | | * +---------+----------------+------------+ * fp->f_addr modulep kernend * * Xen dom0 layout: * fp->f_addr fp->f_addr + fp->f_size * +---------+------------+----------------+------------+ * | | | | | * | Kernel | xen_header | Modules | Metadata | * | | | | | * +---------+------------+----------------+------------+ * modulep kernend * \________/\__________________________________________/ * module 0 module 1 */ fp = file_findfile(NULL, "elf kernel"); if (fp == NULL) { printf("No FreeBSD kernel provided, aborting\n"); error = EINVAL; goto error; } error = bi_load(fp->f_args, &modulep, &kernend, false); if (error != 0) goto error; /* * Note that the Xen kernel requires to be started with BootServices * enabled, and hence we cannot use efi_copy_finish to relocate the * loaded data from the staging area to the expected loaded addresses. * This is fine because the Xen kernel is relocatable, so it can boot * fine straight from the staging area. We use efi_translate to get the * staging addresses where the kernels and metadata are currently * loaded. */ kern_base = (uintptr_t)efi_translate(fp->f_addr); payload_base = kern_base + fp->f_size - PAGE_SIZE; multiboot_space += add_module(multiboot_space, kern_base, payload_base, NULL); multiboot_space += add_module(multiboot_space, payload_base, (uintptr_t)efi_translate(kernend), "header"); header.flags = XENHEADER_HAS_MODULEP_OFFSET; header.modulep_offset = modulep - (fp->f_addr + fp->f_size - PAGE_SIZE); archsw.arch_copyin(&header, fp->f_addr + fp->f_size - PAGE_SIZE, sizeof(header)); multiboot_space += add_end(multiboot_space); total_size = (uint32_t *)(uintptr_t)(addr); *total_size = (uintptr_t)multiboot_space - addr; if (*total_size > PAGE_SIZE) panic("Multiboot header exceeds fixed size"); efi_time_fini(); dev_cleanup(); multiboot2_exec(efi_translate(hdr->efi64_entry), addr, stack + 128 * 1024); panic("exec returned"); error: if (addr) BS->FreePages(addr, EFI_SIZE_TO_PAGES(PAGE_SIZE)); if (stack) BS->FreePages(stack, EFI_SIZE_TO_PAGES(128 * 1024)); return (error); } static int obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) { struct preloaded_file *mfp, *kfp, *rfp; struct kernel_module *kmp; int error; /* See if there's a multiboot kernel loaded */ mfp = file_findfile(NULL, "elf multiboot kernel"); if (mfp == NULL) return (EFTYPE); /* * We have a multiboot kernel loaded, see if there's a FreeBSD * kernel loaded also. */ kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) { /* * No kernel loaded, this must be it. The kernel has to * be loaded as a raw file, it will be processed by * Xen and correctly loaded as an ELF file. */ rfp = file_loadraw(filename, "elf kernel", 0); if (rfp == NULL) { printf( "Unable to load %s as a multiboot payload kernel\n", filename); return (EINVAL); } /* Load kernel metadata... */ setenv("kernelname", filename, 1); error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); if (error) { printf("Unable to load kernel %s metadata error: %d\n", rfp->f_name, error); return (EINVAL); } /* * Reserve one page at the end of the kernel to place some * metadata in order to cope for Xen relocating the modules and * the metadata information. */ rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); rfp->f_size += PAGE_SIZE; *result = rfp; } else { /* The rest should be loaded as regular modules */ error = elf64_obj_loadfile(filename, dest, result); if (error != 0) { printf("Unable to load %s as an object file, error: %d", filename, error); return (error); } } return (0); } static int obj_exec(struct preloaded_file *fp) { return (EFTYPE); } struct file_format multiboot2 = { loadfile, exec }; struct file_format multiboot2_obj = { obj_loadfile, obj_exec }; diff --git a/stand/i386/libi386/multiboot.c b/stand/i386/libi386/multiboot.c index e11da0444fcd..b69895de9706 100644 --- a/stand/i386/libi386/multiboot.c +++ b/stand/i386/libi386/multiboot.c @@ -1,386 +1,386 @@ /*- * Copyright (c) 2014 Roger Pau Monné * 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. */ /* * This multiboot implementation only implements a subset of the full * multiboot specification in order to be able to boot Xen and a * FreeBSD Dom0. Trying to use it to boot other multiboot compliant * kernels will most surely fail. * * The full multiboot specification can be found here: * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html */ #include #include #include #include #include #define _MACHINE_ELF_WANT_32BIT #include #include #include #include #include "bootstrap.h" #include "multiboot.h" #include "libi386.h" #include #define MULTIBOOT_SUPPORTED_FLAGS \ (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO) #define NUM_MODULES 2 extern int elf32_loadfile_raw(char *filename, uint64_t dest, struct preloaded_file **result, int multiboot); extern int elf64_load_modmetadata(struct preloaded_file *fp, uint64_t dest); extern int elf64_obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result); static int multiboot_loadfile(char *, uint64_t, struct preloaded_file **); static int multiboot_exec(struct preloaded_file *); static int multiboot_obj_loadfile(char *, uint64_t, struct preloaded_file **); static int multiboot_obj_exec(struct preloaded_file *fp); struct file_format multiboot = { multiboot_loadfile, multiboot_exec }; struct file_format multiboot_obj = { multiboot_obj_loadfile, multiboot_obj_exec }; extern void multiboot_tramp(); static const char mbl_name[] = "FreeBSD Loader"; static int multiboot_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) { uint32_t *magic; int i, error; caddr_t header_search; ssize_t search_size; int fd; struct multiboot_header *header; char *cmdline; /* * Read MULTIBOOT_SEARCH size in order to search for the * multiboot magic header. */ if (filename == NULL) return (EFTYPE); if ((fd = open(filename, O_RDONLY)) == -1) return (errno); header_search = malloc(MULTIBOOT_SEARCH); if (header_search == NULL) { close(fd); return (ENOMEM); } search_size = read(fd, header_search, MULTIBOOT_SEARCH); magic = (uint32_t *)header_search; header = NULL; for (i = 0; i < (search_size / sizeof(uint32_t)); i++) { if (magic[i] == MULTIBOOT_HEADER_MAGIC) { header = (struct multiboot_header *)&magic[i]; break; } } if (header == NULL) { error = EFTYPE; goto out; } /* Valid multiboot header has been found, validate checksum */ if (header->magic + header->flags + header->checksum != 0) { printf( "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n", header->magic, header->flags, header->checksum); error = EFTYPE; goto out; } if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) { printf("Unsupported multiboot flags found: 0x%x\n", header->flags); error = EFTYPE; goto out; } error = elf32_loadfile_raw(filename, dest, result, 1); if (error != 0) { printf( "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", error); goto out; } /* * f_addr is already aligned to PAGE_SIZE, make sure * f_size it's also aligned so when the modules are loaded * they are aligned to PAGE_SIZE. */ (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); out: free(header_search); close(fd); return (error); } static int multiboot_exec(struct preloaded_file *fp) { vm_offset_t modulep, kernend, entry; struct file_metadata *md; Elf_Ehdr *ehdr; struct multiboot_info *mb_info = NULL; struct multiboot_mod_list *mb_mod = NULL; char *cmdline = NULL; size_t len; int error, mod_num; struct xen_header header; - CTASSERT(sizeof(header) <= PAGE_SIZE); + _Static_assert(sizeof(header) <= PAGE_SIZE, "header too large for page"); /* * Don't pass the memory size found by the bootloader, the memory * available to Dom0 will be lower than that. */ unsetenv("smbios.memory.enabled"); /* Allocate the multiboot struct and fill the basic details. */ mb_info = malloc(sizeof(struct multiboot_info)); if (mb_info == NULL) { error = ENOMEM; goto error; } bzero(mb_info, sizeof(struct multiboot_info)); mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; mb_info->mem_lower = bios_basemem / 1024; mb_info->mem_upper = bios_extmem / 1024; mb_info->boot_loader_name = VTOP(mbl_name); /* Set the Xen command line. */ if (fp->f_args == NULL) { /* Add the Xen command line if it is set. */ cmdline = getenv("xen_cmdline"); if (cmdline != NULL) { fp->f_args = strdup(cmdline); if (fp->f_args == NULL) { error = ENOMEM; goto error; } } } if (fp->f_args != NULL) { len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; cmdline = malloc(len); if (cmdline == NULL) { error = ENOMEM; goto error; } snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); mb_info->cmdline = VTOP(cmdline); mb_info->flags |= MULTIBOOT_INFO_CMDLINE; } /* Find the entry point of the Xen kernel and save it for later */ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { printf("Unable to find %s entry point\n", fp->f_name); error = EINVAL; goto error; } ehdr = (Elf_Ehdr *)&(md->md_data); entry = ehdr->e_entry & 0xffffff; /* * Prepare the multiboot module list, Xen assumes the first * module is the Dom0 kernel, and the second one is the initramfs. * This is not optimal for FreeBSD, that doesn't have a initramfs * but instead loads modules dynamically and creates the metadata * info on-the-fly. * * As expected, the first multiboot module is going to be the * FreeBSD kernel loaded as a raw file. The second module is going * to contain the metadata info and the loaded modules. * * There's a small header prefixed in the second module that contains * some information required to calculate the relocated address of * modulep based on the original offset of modulep from the start of * the module address. Note other fields might be added to this header * if required. * * Native layout: * fp->f_addr + fp->f_size * +---------+----------------+------------+ * | | | | * | Kernel | Modules | Metadata | * | | | | * +---------+----------------+------------+ * fp->f_addr modulep kernend * * Xen dom0 layout: * fp->f_addr fp->f_addr + fp->f_size * +---------+------------+----------------+------------+ * | | | | | * | Kernel | xen_header | Modules | Metadata | * | | | | | * +---------+------------+----------------+------------+ * modulep kernend * \________/\__________________________________________/ * module 0 module 1 */ fp = file_findfile(NULL, "elf kernel"); if (fp == NULL) { printf("No FreeBSD kernel provided, aborting\n"); error = EINVAL; goto error; } mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES); if (mb_mod == NULL) { error = ENOMEM; goto error; } bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES); error = bi_load64(fp->f_args, &modulep, &kernend, 0); if (error != 0) { printf("bi_load64 failed: %d\n", error); goto error; } mb_mod[0].mod_start = fp->f_addr; mb_mod[0].mod_end = fp->f_addr + fp->f_size - PAGE_SIZE; mb_mod[1].mod_start = mb_mod[0].mod_end; mb_mod[1].mod_end = kernend; /* Indicate the kernel metadata is prefixed with a xen_header. */ cmdline = strdup("header"); if (cmdline == NULL) { printf("Out of memory, aborting\n"); error = ENOMEM; goto error; } mb_mod[1].cmdline = VTOP(cmdline); mb_info->mods_count = NUM_MODULES; mb_info->mods_addr = VTOP(mb_mod); mb_info->flags |= MULTIBOOT_INFO_MODS; header.flags = XENHEADER_HAS_MODULEP_OFFSET; header.modulep_offset = modulep - mb_mod[1].mod_start; archsw.arch_copyin(&header, mb_mod[1].mod_start, sizeof(header)); dev_cleanup(); __exec((void *)VTOP(multiboot_tramp), (void *)entry, (void *)VTOP(mb_info)); panic("exec returned"); error: if (mb_mod) free(mb_mod); if (mb_info) free(mb_info); if (cmdline) free(cmdline); return (error); } static int multiboot_obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) { struct preloaded_file *mfp, *kfp, *rfp; struct kernel_module *kmp; int error, mod_num; /* See if there's a multiboot kernel loaded */ mfp = file_findfile(NULL, "elf multiboot kernel"); if (mfp == NULL) return (EFTYPE); /* * We have a multiboot kernel loaded, see if there's a FreeBSD * kernel loaded also. */ kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) { /* * No kernel loaded, this must be it. The kernel has to * be loaded as a raw file, it will be processed by * Xen and correctly loaded as an ELF file. */ rfp = file_loadraw(filename, "elf kernel", 0); if (rfp == NULL) { printf( "Unable to load %s as a multiboot payload kernel\n", filename); return (EINVAL); } /* Load kernel metadata... */ setenv("kernelname", filename, 1); error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); if (error) { printf("Unable to load kernel %s metadata error: %d\n", rfp->f_name, error); return (EINVAL); } /* * Reserve one page at the end of the kernel to place some * metadata in order to cope for Xen relocating the modules and * the metadata information. */ rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); rfp->f_size += PAGE_SIZE; *result = rfp; } else { /* The rest should be loaded as regular modules */ error = elf64_obj_loadfile(filename, dest, result); if (error != 0) { printf("Unable to load %s as an object file, error: %d", filename, error); return (error); } } return (0); } static int multiboot_obj_exec(struct preloaded_file *fp) { return (EFTYPE); } diff --git a/stand/i386/loader/main.c b/stand/i386/loader/main.c index 97c4448afdc3..8a3c1cbc6a42 100644 --- a/stand/i386/loader/main.c +++ b/stand/i386/loader/main.c @@ -1,454 +1,453 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include /* * MD bootstrap main() and assorted miscellaneous * commands. */ #include #include #include #include #include #include #include #include #include #include "bootstrap.h" #include "common/bootargs.h" #include "libi386/libi386.h" #include #include "btxv86.h" #ifdef LOADER_ZFS_SUPPORT #include #include "libzfs.h" #endif -CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE); -CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO); -CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS); -CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE); +_Static_assert(sizeof(struct bootargs) == BOOTARGS_SIZE, "Bootarg size bad"); +_Static_assert(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO, "BA_BOOTINFO"); +_Static_assert(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS, "BA_BOOTFLAGS"); +_Static_assert(offsetof(struct bootinfo, bi_size) == BI_SIZE, "BI_SIZE"); /* Arguments passed in from the boot1/boot2 loader */ static struct bootargs *kargs; - static uint32_t initial_howto; static uint32_t initial_bootdev; static struct bootinfo *initial_bootinfo; struct arch_switch archsw; /* MI/MD interface boundary */ static void extract_currdev(void); static int isa_inb(int port); static void isa_outb(int port, int value); void exit(int code); #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" struct geli_boot_args *gargs; struct geli_boot_data *gbdata; #endif #ifdef LOADER_ZFS_SUPPORT struct zfs_boot_args *zargs; static void i386_zfs_probe(void); #endif /* XXX debugging */ extern char end[]; static void *heap_top; static void *heap_bottom; caddr_t ptov(uintptr_t x) { return (PTOV(x)); } int main(void) { /* Pick up arguments */ kargs = (void *)__args; initial_howto = kargs->howto; initial_bootdev = kargs->bootdev; initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL; /* Initialize the v86 register set to a known-good state. */ bzero(&v86, sizeof(v86)); v86.efl = PSL_RESERVED_DEFAULT | PSL_I; /* * Initialise the heap as early as possible. * Once this is done, malloc() is usable. */ bios_getmem(); #if defined(LOADER_BZIP2_SUPPORT) || \ defined(LOADER_GPT_SUPPORT) || defined(LOADER_ZFS_SUPPORT) if (high_heap_size > 0) { heap_top = PTOV(high_heap_base + high_heap_size); heap_bottom = PTOV(high_heap_base); if (high_heap_base < memtop_copyin) memtop_copyin = high_heap_base; } else #endif { heap_top = (void *)PTOV(bios_basemem); heap_bottom = (void *)end; } setheap(heap_bottom, heap_top); /* * Now that malloc is usable, allocate a buffer for tslog and start * logging timestamps during the boot process. */ tslog_init(); /* * detect ACPI for future reference. This may set console to comconsole * if we do have ACPI SPCR table. */ biosacpi_detect(); /* * XXX Chicken-and-egg problem; we want to have console output early, * but some console attributes may depend on reading from eg. the boot * device, which we can't do yet. * * We can use printf() etc. once this is done. * If the previous boot stage has requested a serial console, * prefer that. */ bi_setboothowto(initial_howto); if (initial_howto & RB_MULTIPLE) { if (initial_howto & RB_SERIAL) setenv("console", "comconsole vidconsole", 1); else setenv("console", "vidconsole comconsole", 1); } else if (initial_howto & RB_SERIAL) { setenv("console", "comconsole", 1); } else if (initial_howto & RB_MUTE) { setenv("console", "nullconsole", 1); } cons_probe(); /* Set up currdev variable to have hooks in place. */ env_setenv("currdev", EV_VOLATILE | EV_NOHOOK, "", gen_setcurrdev, env_nounset); /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); /* * Special handling for PXE and CD booting. */ if (kargs->bootinfo == 0) { /* * We only want the PXE disk to try to init itself in the below * walk through devsw if we actually booted off of PXE. */ if (kargs->bootflags & KARGS_FLAGS_PXE) pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL); else if (kargs->bootflags & KARGS_FLAGS_CD) bc_add(initial_bootdev); } archsw.arch_autoload = i386_autoload; archsw.arch_getdev = i386_getdev; archsw.arch_copyin = i386_copyin; archsw.arch_copyout = i386_copyout; archsw.arch_readin = i386_readin; archsw.arch_isainb = isa_inb; archsw.arch_isaoutb = isa_outb; archsw.arch_hypervisor = x86_hypervisor; #ifdef LOADER_ZFS_SUPPORT archsw.arch_zfs_probe = i386_zfs_probe; /* * zfsboot and gptzfsboot have always passed KARGS_FLAGS_ZFS, * so if that is set along with KARGS_FLAGS_EXTARG we know we * can interpret the extarg data as a struct zfs_boot_args. */ #define KARGS_EXTARGS_ZFS (KARGS_FLAGS_EXTARG | KARGS_FLAGS_ZFS) if ((kargs->bootflags & KARGS_EXTARGS_ZFS) == KARGS_EXTARGS_ZFS) { zargs = (struct zfs_boot_args *)(kargs + 1); } #endif /* LOADER_ZFS_SUPPORT */ #ifdef LOADER_GELI_SUPPORT /* * If we decided earlier that we have zfs_boot_args extarg data, * and it is big enough to contain the embedded geli data * (the early zfs_boot_args structs weren't), then init the gbdata * pointer accordingly. If there is extarg data which isn't * zfs_boot_args data, determine whether it is geli_boot_args data. * Recent versions of gptboot set KARGS_FLAGS_GELI to indicate that. * Earlier versions didn't, but we presume that's what we * have if the extarg size exactly matches the size of the * geli_boot_args struct during that pre-flag era. */ #define LEGACY_GELI_ARGS_SIZE 260 /* This can never change */ #ifdef LOADER_ZFS_SUPPORT if (zargs != NULL) { if (zargs->size > offsetof(struct zfs_boot_args, gelidata)) { gbdata = &zargs->gelidata; } } else #endif /* LOADER_ZFS_SUPPORT */ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) { gargs = (struct geli_boot_args *)(kargs + 1); if ((kargs->bootflags & KARGS_FLAGS_GELI) || gargs->size == LEGACY_GELI_ARGS_SIZE) { gbdata = &gargs->gelidata; } } if (gbdata != NULL) import_geli_boot_data(gbdata); #endif /* LOADER_GELI_SUPPORT */ devinit(); printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024); if (initial_bootinfo != NULL) { initial_bootinfo->bi_basemem = bios_basemem / 1024; initial_bootinfo->bi_extmem = bios_extmem / 1024; } /* detect SMBIOS for future reference */ smbios_detect(NULL); /* detect PCI BIOS for future reference */ biospci_detect(); printf("\n%s", bootprog_info); extract_currdev(); /* set $currdev and $loaddev */ autoload_font(true); bios_getsmap(); interact(); /* if we ever get here, it is an error */ return (1); } /* * Set the 'current device' by (if possible) recovering the boot device as * supplied by the initial bootstrap. * * XXX should be extended for netbooting. */ static void extract_currdev(void) { struct i386_devdesc new_currdev; #ifdef LOADER_ZFS_SUPPORT char buf[20]; char *bootonce; #endif int biosdev = -1; /* Assume we are booting from a BIOS disk by default */ new_currdev.dd.d_dev = &bioshd; /* new-style boot loaders such as pxeldr and cdldr */ if (kargs->bootinfo == 0) { if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) { /* we are booting from a CD with cdboot */ new_currdev.dd.d_dev = &bioscd; new_currdev.dd.d_unit = bd_bios2unit(initial_bootdev); } else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) { /* we are booting from pxeldr */ new_currdev.dd.d_dev = &pxedisk; new_currdev.dd.d_unit = 0; } else { /* we don't know what our boot device is */ new_currdev.disk.d_slice = -1; new_currdev.disk.d_partition = 0; biosdev = -1; } #ifdef LOADER_ZFS_SUPPORT } else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) { /* * zargs was set in main() if we have new style extended * argument */ if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) { /* sufficient data is provided */ new_currdev.zfs.pool_guid = zargs->pool; new_currdev.zfs.root_guid = zargs->root; if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) { sprintf(buf, "%llu", zargs->primary_pool); setenv("vfs.zfs.boot.primary_pool", buf, 1); sprintf(buf, "%llu", zargs->primary_vdev); setenv("vfs.zfs.boot.primary_vdev", buf, 1); } } else { /* old style zfsboot block */ new_currdev.zfs.pool_guid = kargs->zfspool; new_currdev.zfs.root_guid = 0; } new_currdev.dd.d_dev = &zfs_dev; if ((bootonce = malloc(VDEV_PAD_SIZE)) != NULL) { if (zfs_get_bootonce(&new_currdev, OS_BOOTONCE_USED, bootonce, VDEV_PAD_SIZE) == 0) { setenv("zfs-bootonce", bootonce, 1); } free(bootonce); (void) zfs_attach_nvstore(&new_currdev); } #endif } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) { /* The passed-in boot device is bad */ new_currdev.disk.d_slice = -1; new_currdev.disk.d_partition = 0; biosdev = -1; } else { new_currdev.disk.d_slice = B_SLICE(initial_bootdev) - 1; new_currdev.disk.d_partition = B_PARTITION(initial_bootdev); biosdev = initial_bootinfo->bi_bios_dev; /* * If we are booted by an old bootstrap, we have to guess at * the BIOS unit number. We will lose if there is more than * one disk type and we are not booting from the * lowest-numbered disk type (ie. SCSI when IDE also exists). */ if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2)) { /* * biosdev doesn't match major, assume harddisk */ biosdev = 0x80 + B_UNIT(initial_bootdev); } } /* * If we are booting off of a BIOS disk and we didn't succeed * in determining which one we booted off of, just use disk0: * as a reasonable default. */ if ((new_currdev.dd.d_dev->dv_type == bioshd.dv_type) && ((new_currdev.dd.d_unit = bd_bios2unit(biosdev)) == -1)) { printf("Can't work out which disk we are booting " "from.\nGuessed BIOS device 0x%x not found by " "probes, defaulting to disk0:\n", biosdev); new_currdev.dd.d_unit = 0; } #ifdef LOADER_ZFS_SUPPORT if (new_currdev.dd.d_dev->dv_type == DEVT_ZFS) init_zfs_boot_options(devformat(&new_currdev.dd)); #endif set_currdev(devformat(&new_currdev.dd)); } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); printf("Rebooting...\n"); delay(1000000); __exit(0); } /* provide this for panic, as it's not in the startup code */ void exit(int code) { __exit(code); } COMMAND_SET(heap, "heap", "show heap usage", command_heap); static int command_heap(int argc, char *argv[]) { mallocstats(); printf("heap base at %p, top at %p, upper limit at %p\n", heap_bottom, sbrk(0), heap_top); return (CMD_OK); } /* ISA bus access functions for PnP. */ static int isa_inb(int port) { return (inb(port)); } static void isa_outb(int port, int value) { outb(port, value); } #ifdef LOADER_ZFS_SUPPORT static void i386_zfs_probe(void) { char devname[32]; struct i386_devdesc dev; /* * Open all the disks we can find and see if we can reconstruct * ZFS pools from them. */ dev.dd.d_dev = &bioshd; for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) { snprintf(devname, sizeof(devname), "%s%d:", bioshd.dv_name, dev.dd.d_unit); zfs_probe_dev(devname, NULL, true); } } #endif