Index: head/sys/boot/efi/loader/main.c =================================================================== --- head/sys/boot/efi/loader/main.c (revision 320010) +++ head/sys/boot/efi/loader/main.c (revision 320011) @@ -1,839 +1,933 @@ /*- * Copyright (c) 2008-2010 Rui Paulo * Copyright (c) 2006 Marcel Moolenaar * 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 ``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 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EFI_ZFS_BOOT #include #endif #include "loader_efi.h" extern char bootprog_info[]; struct arch_switch archsw; /* MI/MD interface boundary */ EFI_GUID acpi = ACPI_TABLE_GUID; EFI_GUID acpi20 = ACPI_20_TABLE_GUID; EFI_GUID devid = DEVICE_PATH_PROTOCOL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID mps = MPS_TABLE_GUID; EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; EFI_GUID hoblist = HOB_LIST_TABLE_GUID; EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void); static uint64_t pool_guid; #endif static int has_keyboard(void) { EFI_STATUS status; EFI_DEVICE_PATH *path; EFI_HANDLE *hin, *hin_end, *walker; UINTN sz; int retval = 0; /* * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and * do the typical dance to get the right sized buffer. */ sz = 0; hin = NULL; status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { hin = (EFI_HANDLE *)malloc(sz); status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return retval; /* * Look at each of the handles. If it supports the device path protocol, * use it to get the device path for this handle. Then see if that * device path matches either the USB device path for keyboards or the * legacy device path for keyboards. */ hin_end = &hin[sz / sizeof(*hin)]; for (walker = hin; walker < hin_end; walker++) { status = BS->HandleProtocol(*walker, &devid, (VOID **)&path); if (EFI_ERROR(status)) continue; while (!IsDevicePathEnd(path)) { /* * Check for the ACPI keyboard node. All PNP3xx nodes * are keyboards of different flavors. Note: It is * unclear of there's always a keyboard node when * there's a keyboard controller, or if there's only one * when a keyboard is detected at boot. */ if (DevicePathType(path) == ACPI_DEVICE_PATH && (DevicePathSubType(path) == ACPI_DP || DevicePathSubType(path) == ACPI_EXTENDED_DP)) { ACPI_HID_DEVICE_PATH *acpi; acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { retval = 1; goto out; } /* * Check for USB keyboard node, if present. Unlike a * PS/2 keyboard, these definitely only appear when * connected to the system. */ } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && DevicePathSubType(path) == MSG_USB_CLASS_DP) { USB_CLASS_DEVICE_PATH *usb; usb = (USB_CLASS_DEVICE_PATH *)(void *)path; if (usb->DeviceClass == 3 && /* HID */ usb->DeviceSubClass == 1 && /* Boot devices */ usb->DeviceProtocol == 1) { /* Boot keyboards */ retval = 1; goto out; } } path = NextDevicePathNode(path); } } out: free(hin); return retval; } static void set_devdesc_currdev(struct devsw *dev, int unit) { struct devdesc currdev; char *devname; currdev.d_dev = dev; currdev.d_type = currdev.d_dev->dv_type; currdev.d_unit = unit; currdev.d_opendata = NULL; devname = efi_fmtdev(&currdev); env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); } static int find_currdev(EFI_LOADED_IMAGE *img) { pdinfo_list_t *pdi_list; pdinfo_t *dp, *pp; EFI_DEVICE_PATH *devpath, *copy; EFI_HANDLE h; char *devname; struct devsw *dev; int unit; uint64_t extra; #ifdef EFI_ZFS_BOOT /* Did efi_zfs_probe() detect the boot pool? */ if (pool_guid != 0) { struct zfs_devdesc currdev; currdev.d_dev = &zfs_dev; currdev.d_unit = 0; currdev.d_type = currdev.d_dev->dv_type; currdev.d_opendata = NULL; currdev.pool_guid = pool_guid; currdev.root_guid = 0; devname = efi_fmtdev(&currdev); env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); init_zfs_bootenv(devname); return (0); } #endif /* EFI_ZFS_BOOT */ /* We have device lists for hd, cd, fd, walk them all. */ pdi_list = efiblk_get_pdinfo_list(&efipart_hddev); STAILQ_FOREACH(dp, pdi_list, pd_link) { struct disk_devdesc currdev; currdev.d_dev = &efipart_hddev; currdev.d_type = currdev.d_dev->dv_type; currdev.d_unit = dp->pd_unit; currdev.d_opendata = NULL; currdev.d_slice = -1; currdev.d_partition = -1; if (dp->pd_handle == img->DeviceHandle) { devname = efi_fmtdev(&currdev); env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); return (0); } /* Assuming GPT partitioning. */ STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { if (pp->pd_handle == img->DeviceHandle) { currdev.d_slice = pp->pd_unit; currdev.d_partition = 255; devname = efi_fmtdev(&currdev); env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); return (0); } } } pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); STAILQ_FOREACH(dp, pdi_list, pd_link) { if (dp->pd_handle == img->DeviceHandle || dp->pd_alias == img->DeviceHandle) { set_devdesc_currdev(&efipart_cddev, dp->pd_unit); return (0); } } pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); STAILQ_FOREACH(dp, pdi_list, pd_link) { if (dp->pd_handle == img->DeviceHandle) { set_devdesc_currdev(&efipart_fddev, dp->pd_unit); return (0); } } /* * Try the device handle from our loaded image first. If that * fails, use the device path from the loaded image and see if * any of the nodes in that path match one of the enumerated * handles. */ if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) { set_devdesc_currdev(dev, unit); return (0); } copy = NULL; devpath = efi_lookup_image_devpath(IH); while (devpath != NULL) { h = efi_devpath_handle(devpath); if (h == NULL) break; free(copy); copy = NULL; if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) { set_devdesc_currdev(dev, unit); return (0); } devpath = efi_lookup_devpath(h); if (devpath != NULL) { copy = efi_devpath_trim(devpath); devpath = copy; } } free(copy); return (ENOENT); } EFI_STATUS main(int argc, CHAR16 *argv[]) { char var[128]; EFI_LOADED_IMAGE *img; EFI_GUID *guid; int i, j, vargood, howto; UINTN k; int has_kbd; char buf[40]; archsw.arch_autoload = efi_autoload; archsw.arch_getdev = efi_getdev; archsw.arch_copyin = efi_copyin; archsw.arch_copyout = efi_copyout; archsw.arch_readin = efi_readin; #ifdef EFI_ZFS_BOOT /* Note this needs to be set before ZFS init. */ archsw.arch_zfs_probe = efi_zfs_probe; #endif /* Init the time source */ efi_time_init(); has_kbd = has_keyboard(); /* * 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. */ cons_probe(); /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); /* * Parse the args to set the console settings, etc * boot1.efi passes these in, if it can read /boot.config or /boot/config * or iPXE may be setup to pass these in. * * Loop through the args, and for each one that contains an '=' that is * not the first character, add it to the environment. This allows * loader and kernel env vars to be passed on the command line. Convert * args from UCS-2 to ASCII (16 to 8 bit) as they are copied. */ howto = 0; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (j = 1; argv[i][j] != 0; j++) { int ch; ch = argv[i][j]; switch (ch) { case 'a': howto |= RB_ASKNAME; break; case 'd': howto |= RB_KDB; break; case 'D': howto |= RB_MULTIPLE; break; case 'h': howto |= RB_SERIAL; break; case 'm': howto |= RB_MUTE; break; case 'p': howto |= RB_PAUSE; break; case 'P': if (!has_kbd) howto |= RB_SERIAL | RB_MULTIPLE; break; case 'r': howto |= RB_DFLTROOT; break; case 's': howto |= RB_SINGLE; break; case 'S': if (argv[i][j + 1] == 0) { if (i + 1 == argc) { setenv("comconsole_speed", "115200", 1); } else { cpy16to8(&argv[i + 1][0], var, sizeof(var)); setenv("comconsole_speed", var, 1); } i++; break; } else { cpy16to8(&argv[i][j + 1], var, sizeof(var)); setenv("comconsole_speed", var, 1); break; } case 'v': howto |= RB_VERBOSE; break; } } } else { vargood = 0; for (j = 0; argv[i][j] != 0; j++) { if (j == sizeof(var)) { vargood = 0; break; } if (j > 0 && argv[i][j] == '=') vargood = 1; var[j] = (char)argv[i][j]; } if (vargood) { var[j] = 0; putenv(var); } } } for (i = 0; howto_names[i].ev != NULL; i++) if (howto & howto_names[i].mask) setenv(howto_names[i].ev, "YES", 1); if (howto & RB_MULTIPLE) { if (howto & RB_SERIAL) setenv("console", "comconsole efi" , 1); else setenv("console", "efi comconsole" , 1); } else if (howto & RB_SERIAL) { setenv("console", "comconsole" , 1); } if (efi_copy_init()) { printf("failed to allocate staging area\n"); return (EFI_BUFFER_TOO_SMALL); } /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); /* Get our loaded image protocol interface structure. */ BS->HandleProtocol(IH, &imgid, (VOID**)&img); printf("Command line arguments:"); for (i = 0; i < argc; i++) printf(" %S", argv[i]); printf("\n"); printf("Image base: 0x%lx\n", (u_long)img->ImageBase); printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); printf("\n%s", bootprog_info); /* * Disable the watchdog timer. By default the boot manager sets * the timer to 5 minutes before invoking a boot option. If we * want to return to the boot manager, we have to disable the * watchdog timer and since we're an interactive program, we don't * want to wait until the user types "quit". The timer may have * fired by then. We don't care if this fails. It does not prevent * normal functioning in any way... */ BS->SetWatchdogTimer(0, 0, 0, NULL); if (find_currdev(img) != 0) return (EFI_NOT_FOUND); efi_init_environment(); setenv("LINES", "24", 1); /* optional */ for (k = 0; k < ST->NumberOfTableEntries; k++) { guid = &ST->ConfigurationTable[k].VendorGuid; if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { snprintf(buf, sizeof(buf), "%p", ST->ConfigurationTable[k].VendorTable); setenv("hint.smbios.0.mem", buf, 1); smbios_detect(ST->ConfigurationTable[k].VendorTable); break; } } interact(NULL); /* doesn't return */ return (EFI_SUCCESS); /* keep compiler happy */ } 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)(); RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(quit, "quit", "exit the loader", command_quit); static int command_quit(int argc, char *argv[]) { exit(0); return (CMD_OK); } COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); static int command_memmap(int argc, char *argv[]) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map, *p; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; char line[80]; static char *types[] = { "Reserved", "LoaderCode", "LoaderData", "BootServicesCode", "BootServicesData", "RuntimeServicesCode", "RuntimeServicesData", "ConventionalMemory", "UnusableMemory", "ACPIReclaimMemory", "ACPIMemoryNVS", "MemoryMappedIO", "MemoryMappedIOPortSpace", "PalCode" }; sz = 0; status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); if (status != EFI_BUFFER_TOO_SMALL) { printf("Can't determine memory map size\n"); return (CMD_ERROR); } map = malloc(sz); status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (EFI_ERROR(status)) { printf("Can't read memory map\n"); return (CMD_ERROR); } ndesc = sz / dsz; snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n", "Type", "Physical", "Virtual", "#Pages", "Attr"); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { printf("%23s %012jx %012jx %08jx ", types[p->Type], (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, (uintmax_t)p->NumberOfPages); if (p->Attribute & EFI_MEMORY_UC) printf("UC "); if (p->Attribute & EFI_MEMORY_WC) printf("WC "); if (p->Attribute & EFI_MEMORY_WT) printf("WT "); if (p->Attribute & EFI_MEMORY_WB) printf("WB "); if (p->Attribute & EFI_MEMORY_UCE) printf("UCE "); if (p->Attribute & EFI_MEMORY_WP) printf("WP "); if (p->Attribute & EFI_MEMORY_RP) printf("RP "); if (p->Attribute & EFI_MEMORY_XP) printf("XP "); if (pager_output("\n")) break; } pager_close(); return (CMD_OK); } COMMAND_SET(configuration, "configuration", "print configuration tables", command_configuration); static const char * guid_to_string(EFI_GUID *guid) { static char buf[40]; sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); return (buf); } static int command_configuration(int argc, char *argv[]) { char line[80]; UINTN i; snprintf(line, sizeof(line), "NumberOfTableEntries=%lu\n", (unsigned long)ST->NumberOfTableEntries); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0; i < ST->NumberOfTableEntries; i++) { EFI_GUID *guid; printf(" "); guid = &ST->ConfigurationTable[i].VendorGuid; if (!memcmp(guid, &mps, sizeof(EFI_GUID))) printf("MPS Table"); else if (!memcmp(guid, &acpi, sizeof(EFI_GUID))) printf("ACPI Table"); else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID))) printf("ACPI 2.0 Table"); else if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) printf("SMBIOS Table %p", ST->ConfigurationTable[i].VendorTable); else if (!memcmp(guid, &dxe, sizeof(EFI_GUID))) printf("DXE Table"); else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID))) printf("HOB List Table"); else if (!memcmp(guid, &memtype, sizeof(EFI_GUID))) printf("Memory Type Information Table"); else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID))) printf("Debug Image Info Table"); else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID))) printf("FDT Table"); else printf("Unknown Table (%s)", guid_to_string(guid)); snprintf(line, sizeof(line), " at %p\n", ST->ConfigurationTable[i].VendorTable); if (pager_output(line)) break; } pager_close(); return (CMD_OK); } COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); static int command_mode(int argc, char *argv[]) { UINTN cols, rows; unsigned int mode; int i; char *cp; char rowenv[8]; EFI_STATUS status; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; extern void HO(void); conout = ST->ConOut; if (argc > 1) { mode = strtol(argv[1], &cp, 0); if (cp[0] != '\0') { printf("Invalid mode\n"); return (CMD_ERROR); } status = conout->QueryMode(conout, mode, &cols, &rows); if (EFI_ERROR(status)) { printf("invalid mode %d\n", mode); return (CMD_ERROR); } status = conout->SetMode(conout, mode); if (EFI_ERROR(status)) { printf("couldn't set mode %d\n", mode); return (CMD_ERROR); } sprintf(rowenv, "%u", (unsigned)rows); setenv("LINES", rowenv, 1); HO(); /* set cursor */ return (CMD_OK); } printf("Current mode: %d\n", conout->Mode->Mode); for (i = 0; i <= conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, (unsigned)rows); } if (i != 0) printf("Select a mode with the command \"mode \"\n"); return (CMD_OK); } #ifdef EFI_ZFS_BOOT COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", command_lszfs); static int command_lszfs(int argc, char *argv[]) { int err; if (argc != 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } err = zfs_list(argv[1]); if (err != 0) { command_errmsg = strerror(err); return (CMD_ERROR); } return (CMD_OK); } COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", command_reloadbe); static int command_reloadbe(int argc, char *argv[]) { int err; char *root; if (argc > 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } if (argc == 2) { err = zfs_bootenv(argv[1]); } else { root = getenv("zfs_be_root"); if (root == NULL) { return (CMD_OK); } err = zfs_bootenv(root); } if (err != 0) { command_errmsg = strerror(err); return (CMD_ERROR); } return (CMD_OK); } #endif #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, 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) { pdinfo_list_t *hdi; pdinfo_t *hd, *pd = NULL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_LOADED_IMAGE *img; char devname[SPECNAMELEN + 1]; BS->HandleProtocol(IH, &imgid, (VOID**)&img); hdi = efiblk_get_pdinfo_list(&efipart_hddev); /* * Find the handle for the boot device. The boot1 did find the * device with loader binary, now we need to search for the * same device and if it is part of the zfs pool, we record the * pool GUID for currdev setup. */ STAILQ_FOREACH(hd, hdi, pd_link) { STAILQ_FOREACH(pd, &hd->pd_part, pd_link) { snprintf(devname, sizeof(devname), "%s%dp%d:", efipart_hddev.dv_name, hd->pd_unit, pd->pd_unit); if (pd->pd_handle == img->DeviceHandle) (void) zfs_probe_dev(devname, &pool_guid); else (void) zfs_probe_dev(devname, NULL); } } } uint64_t ldi_get_size(void *priv) { int fd = (uintptr_t) priv; uint64_t size; ioctl(fd, DIOCGMEDIASIZE, &size); return (size); } #endif Index: head/sys/boot/forth/menu.rc =================================================================== --- head/sys/boot/forth/menu.rc (revision 320010) +++ head/sys/boot/forth/menu.rc (revision 320011) @@ -1,187 +1,202 @@ \ Menu.rc \ $FreeBSD$ \ \ You should not edit this file! Put any overrides in menu.rc.local \ instead as this file can be replaced during system updates. \ \ Load required Forth modules include /boot/version.4th include /boot/brand.4th include /boot/menu.4th include /boot/menu-commands.4th include /boot/shortcuts.4th \ Screen prep clear \ clear the screen (see `screen.4th') print_version \ print version string (bottom-right; see `version.4th') draw-beastie \ draw freebsd mascot (on right; see `beastie.4th') draw-brand \ draw the FreeBSD title (top-left; see `brand.4th') menu-init \ initialize the menu area (see `menu.4th') \ Initialize main menu constructs (see `menu.4th') \ NOTE: To use `non-ansi' variants, add `loader_color=0' to loader.conf(5) \ NOTE: ANSI variants can use `^' in place of literal `Esc' (ASCII 27) \ \ MAIN MENU \ set menuset_name1="main" set mainmenu_init[1]="init_boot" set mainmenu_caption[1]="Boot Multi User [Enter]" set maintoggled_text[1]="Boot [S]ingle User [Enter]" set mainmenu_command[1]="boot" set mainansi_caption[1]="^[1mB^[moot Multi User ^[1m[Enter]^[m" set maintoggled_ansi[1]="Boot ^[1mS^[mingle User ^[1m[Enter]^[m" \ keycode set by init_boot set mainmenu_init[2]="init_altboot" set mainmenu_caption[2]="Boot [S]ingle User" set maintoggled_text[2]="Boot [M]ulti User" set mainmenu_command[2]="altboot" set mainansi_caption[2]="Boot ^[1mS^[mingle User" set maintoggled_ansi[2]="Boot ^[1mM^[multi User" \ keycode set by init_altboot set mainmenu_caption[3]="[Esc]ape to loader prompt" set mainmenu_command[3]="goto_prompt" set mainmenu_keycode[3]=27 set mainansi_caption[3]="^[1mEsc^[mape to loader prompt" \ Enable built-in "Reboot" trailing menuitem \ NOTE: appears before menu_options if configured \ set mainmenu_reboot \ Enable "Options:" separator. When set to a numerical value (1-8), a visual \ separator is inserted before that menuitem number. \ set mainmenu_options=5 set mainmenu_kernel=5 set mainmenu_command[5]="cycle_kernel" set mainmenu_keycode[5]=107 set mainmenu_caption[6]="Configure Boot [O]ptions..." set mainmenu_command[6]="2 goto_menu" set mainmenu_keycode[6]=111 set mainansi_caption[6]="Configure Boot ^[1mO^[mptions..." s" currdev" getenv dup 0> [if] drop 4 s" zfs:" compare 0= [if] set mainmenu_caption[7]="Select Boot [E]nvironment..." 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 \ set menuset_name2="options" set optionsmenu_caption[1]="Back to Main Menu [Backspace]" set optionsmenu_command[1]="1 goto_menu" set optionsmenu_keycode[1]=8 set optionsansi_caption[1]="Back to Main Menu ^[1m[Backspace]^[m" set optionsmenu_caption[2]="Load System [D]efaults" set optionsmenu_command[2]="set_default_boot_options" set optionsmenu_keycode[2]=100 set optionsansi_caption[2]="Load System ^[1mD^[mefaults" set optionsmenu_options=3 set optionsmenu_optionstext="Boot Options:" set optionsmenu_acpi=3 set optionsmenu_caption[3]="[A]CPI Support off" set optionstoggled_text[3]="[A]CPI Support On" set optionsmenu_command[3]="toggle_acpi" set optionsmenu_keycode[3]=97 set optionsansi_caption[3]="^[1mA^[mCPI Support ^[34;1mOff^[m" set optionstoggled_ansi[3]="^[1mA^[mCPI Support ^[32;7mOn^[m" set optionsmenu_init[4]="init_safemode" set optionsmenu_caption[4]="Safe [M]ode... off" set optionstoggled_text[4]="Safe [M]ode... On" set optionsmenu_command[4]="toggle_safemode" set optionsmenu_keycode[4]=109 set optionsansi_caption[4]="Safe ^[1mM^[mode... ^[34;1mOff^[m" set optionstoggled_ansi[4]="Safe ^[1mM^[mode... ^[32;7mOn^[m" set optionsmenu_init[5]="init_singleuser" set optionsmenu_caption[5]="[S]ingle User. off" set optionstoggled_text[5]="[S]ingle User. On" set optionsmenu_command[5]="toggle_singleuser" set optionsmenu_keycode[5]=115 set optionsansi_caption[5]="^[1mS^[mingle User. ^[34;1mOff^[m" set optionstoggled_ansi[5]="^[1mS^[mingle User. ^[32;7mOn^[m" set optionsmenu_init[6]="init_verbose" set optionsmenu_caption[6]="[V]erbose..... off" set optionstoggled_text[6]="[V]erbose..... On" set optionsmenu_command[6]="toggle_verbose" set optionsmenu_keycode[6]=118 set optionsansi_caption[6]="^[1mV^[merbose..... ^[34;1mOff^[m" set optionstoggled_ansi[6]="^[1mV^[merbose..... ^[32;7mOn^[m" \ \ BOOT ENVIRONMENT MENU \ set menuset_name3="bootenv" set bemenu_current="Active: " set beansi_current="^[1m${bemenu_current}^[m" set bemenu_bootfs="bootfs: " set beansi_bootfs="^[1m${bemenu_bootfs}^[m" set bemenu_page="[P]age: " set beansi_page="^[1mP^[mage: " set bemenu_pageof=" of " set beansi_pageof="${bemenu_pageof}" set zfs_be_currpage=1 set bootenvmenu_init="init_bootenv" set bootenvmenu_command[1]="be_draw_screen 1 goto_menu" set bootenvmenu_keycode[1]=8 set bootenvmenu_command[2]="set_bootenv" set bootenvmenu_keycode[2]=97 set bootenv_root[2]="${zfs_be_active}" set bootenvmenu_command[3]="set_be_page" set bootenvmenu_keycode[3]=112 set bootenvmenu_options=4 set bootenvmenu_optionstext="Boot Environments:" \ Enable automatic booting (add ``autoboot_delay=N'' to loader.conf(5) to \ customize the timeout; default is 10-seconds) \ set menu_timeout_command="boot" \ Include optional elements defined in a local file \ try-include /boot/menu.rc.local \ Initialize boot environment variables \ s" reloadbe" sfind ( xt|0 bool ) [if] s" bootenv_autolist" getenv dup -1 = [if] drop s" execute" evaluate \ Use evaluate to avoid passing \ reloadbe an optional parameter [else] s" YES" compare-insensitive 0= [if] s" execute" evaluate [then] [then] [else] drop ( xt=0 ) [then] \ Display the main menu (see `menu.4th') set menuset_initial=1 menuset-loadinitial menu-display Index: head/sys/boot/i386/libi386/Makefile =================================================================== --- head/sys/boot/i386/libi386/Makefile (revision 320010) +++ head/sys/boot/i386/libi386/Makefile (revision 320011) @@ -1,82 +1,82 @@ # $FreeBSD$ # LIB= i386 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 SRCS+= devicename_stubs.c # Enable PXE TFTP or NFS support, not both. .if defined(LOADER_TFTP_SUPPORT) CFLAGS+= -DLOADER_TFTP_SUPPORT .else CFLAGS+= -DLOADER_NFS_SUPPORT .endif BOOT_COMCONSOLE_PORT?= 0x3f8 CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT} BOOT_COMCONSOLE_SPEED?= 9600 CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED} .ifdef(BOOT_BIOSDISK_DEBUG) # Make the disk code more talkative CFLAGS+= -DDISK_DEBUG .endif .if !defined(LOADER_NO_GELI_SUPPORT) # Decrypt encrypted drives CFLAGS+= -DLOADER_GELI_SUPPORT CFLAGS+= -I${.CURDIR}/../../geli .endif .if !defined(BOOT_HIDE_SERIAL_NUMBERS) # Export serial numbers, UUID, and asset tag from loader. CFLAGS+= -DSMBIOS_SERIAL_NUMBERS .if defined(BOOT_LITTLE_ENDIAN_UUID) # Use little-endian UUID format as defined in SMBIOS 2.6. CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID .elif defined(BOOT_NETWORK_ENDIAN_UUID) # Use network-endian UUID format for backward compatibility. CFLAGS+= -DSMBIOS_NETWORK_ENDIAN_UUID .endif .endif # Include simple terminal emulation (cons25-compatible) CFLAGS+= -DTERM_EMU # XXX: make alloca() useable CFLAGS+= -Dalloca=__builtin_alloca CFLAGS+= -I${.CURDIR}/../../ficl -I${.CURDIR}/../../ficl/i386 \ -I${.CURDIR}/../../common -I${.CURDIR}/../common \ -I${.CURDIR}/../btx/lib \ -I${.CURDIR}/../../../contrib/dev/acpica/include \ -I${.CURDIR}/../../.. -I. # the location of libstand CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/ # Handle FreeBSD specific %b and %D printf format specifiers CFLAGS+= ${FORMAT_EXTENSIONS} .if ${MACHINE_CPUARCH} == "amd64" CLEANFILES+= machine machine: .NOMETA ln -sf ${.CURDIR}/../../../i386/include machine .endif .include # XXX: clang integrated-as doesn't grok .codeNN directives yet CFLAGS.amd64_tramp.S= ${CLANG_NO_IAS} CFLAGS.multiboot_tramp.S= ${CLANG_NO_IAS} .if ${MACHINE_CPUARCH} == "amd64" beforedepend ${OBJS}: machine .endif Index: head/sys/boot/i386/libi386/libi386.h =================================================================== --- head/sys/boot/i386/libi386/libi386.h (revision 320010) +++ head/sys/boot/i386/libi386/libi386.h (revision 320011) @@ -1,124 +1,153 @@ /*- * 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. * * $FreeBSD$ */ /* * i386 fully-qualified device descriptor. * Note, this must match the 'struct devdesc' declaration * in bootstrap.h and also with struct zfs_devdesc for zfs * support. */ struct i386_devdesc { struct devsw *d_dev; int d_type; int d_unit; union { struct { void *data; int slice; int partition; off_t offset; } biosdisk; struct { void *data; } bioscd; struct { void *data; uint64_t pool_guid; uint64_t root_guid; } zfs; } 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); extern struct devdesc currdev; /* our current device */ #define MAXDEV 31 /* maximum number of distinct devices */ #define MAXBDDEV MAXDEV /* exported devices XXX rename? */ extern struct devsw bioscd; extern struct devsw biosdisk; extern struct devsw pxedisk; extern struct fs_ops pxe_fsops; int bc_add(int biosdev); /* Register CD booted from. */ int bc_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ int bc_bios2unit(int biosdev); /* xlate BIOS device -> bioscd unit */ int bc_unit2bios(int unit); /* xlate bioscd unit -> BIOS device */ uint32_t bd_getbigeom(int bunit); /* return geometry in bootinfo format */ int bd_bios2unit(int biosdev); /* xlate BIOS device -> biosdisk unit */ int bd_unit2bios(int unit); /* xlate biosdisk unit -> BIOS device */ int bd_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len); ssize_t i386_readin(const int fd, vm_offset_t dest, const size_t len); struct preloaded_file; void bios_addsmapdata(struct preloaded_file *); void bios_getsmap(void); void bios_getmem(void); extern uint32_t bios_basemem; /* base memory in bytes */ extern uint32_t bios_extmem; /* extended memory in bytes */ extern vm_offset_t memtop; /* last address of physical memory + 1 */ extern vm_offset_t memtop_copyin; /* memtop less heap size for the cases */ /* when heap is at the top of */ /* extended memory; for other cases */ /* just the same as memtop */ extern uint32_t high_heap_size; /* extended memory region available */ extern vm_offset_t high_heap_base; /* for use as the heap */ void biospci_detect(void); int biospci_find_devclass(uint32_t class, int index, uint32_t *locator); int biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val); uint32_t biospci_locator(int8_t bus, uint8_t device, uint8_t function); int biospci_write_config(uint32_t locator, int offset, int width, uint32_t val); void biosacpi_detect(void); int i386_autoload(void); int bi_getboothowto(char *kargs); void bi_setboothowto(int howto); vm_offset_t bi_copyenv(vm_offset_t addr); int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernend); int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, vm_offset_t *kernend, int add_smap); void pxe_enable(void *pxeinfo); Index: head/sys/boot/i386/libi386/relocater_tramp.S =================================================================== --- head/sys/boot/i386/libi386/relocater_tramp.S (nonexistent) +++ head/sys/boot/i386/libi386/relocater_tramp.S (revision 320011) @@ -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 Property changes on: head/sys/boot/i386/libi386/relocater_tramp.S ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/boot/i386/loader/Makefile =================================================================== --- head/sys/boot/i386/loader/Makefile (revision 320010) +++ head/sys/boot/i386/loader/Makefile (revision 320011) @@ -1,141 +1,141 @@ # $FreeBSD$ .include MK_SSP= no LOADER?= loader PROG= ${LOADER}.sym MAN= INTERNALPROG= NEWVERSWHAT?= "bootstrap loader" x86 VERSION_FILE= ${.CURDIR}/../loader/version LOADER_NET_SUPPORT?= yes 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) CFLAGS+= -DLOADER_FIREWIRE_SUPPORT LIBFIREWIRE= ${.OBJDIR}/../libfirewire/libfirewire.a .endif # Set by zfsloader Makefile .if defined(LOADER_ZFS_SUPPORT) CFLAGS+= -DLOADER_ZFS_SUPPORT LIBZFSBOOT= ${.OBJDIR}/../../zfs/libzfsboot.a .endif .if defined(LOADER_NET_SUPPORT) CFLAGS+= -I${.CURDIR}/../../../../lib/libstand .endif .if defined(LOADER_TFTP_SUPPORT) CFLAGS+= -DLOADER_TFTP_SUPPORT .endif .if defined(LOADER_NFS_SUPPORT) CFLAGS+= -DLOADER_NFS_SUPPORT .endif # Include bcache code. HAVE_BCACHE= yes # Enable PnP and ISA-PnP code. HAVE_PNP= yes HAVE_ISABUS= yes .if ${MK_FORTH} != "no" # Enable BootForth BOOT_FORTH= yes CFLAGS+= -DBOOT_FORTH -I${.CURDIR}/../../ficl -I${.CURDIR}/../../ficl/i386 .if ${MACHINE_CPUARCH} == "amd64" LIBFICL= ${.OBJDIR}/../../ficl32/libficl.a .else LIBFICL= ${.OBJDIR}/../../ficl/libficl.a .endif .endif .if defined(LOADER_BZIP2_SUPPORT) CFLAGS+= -DLOADER_BZIP2_SUPPORT .endif .if !defined(LOADER_NO_GZIP_SUPPORT) CFLAGS+= -DLOADER_GZIP_SUPPORT .endif .if defined(LOADER_NANDFS_SUPPORT) CFLAGS+= -DLOADER_NANDFS_SUPPORT .endif .if !defined(LOADER_NO_GELI_SUPPORT) CFLAGS+= -DLOADER_GELI_SUPPORT CFLAGS+= -I${.CURDIR}/../../geli LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a .PATH: ${.CURDIR}/../../../opencrypto SRCS+= xform_aes_xts.c CFLAGS+= -I${.CURDIR}/../../.. -D_STAND .endif # Always add MI sources .PATH: ${.CURDIR}/../../common .include "${.CURDIR}/../../common/Makefile.inc" CFLAGS+= -I${.CURDIR}/../../common CFLAGS+= -I. CLEANFILES= ${LOADER} ${LOADER}.bin loader.help CFLAGS+= -Wall LDFLAGS= -static -Ttext 0x0 # i386 standalone support library LIBI386= ${.OBJDIR}/../libi386/libi386.a CFLAGS+= -I${.CURDIR}/.. LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a # BTX components CFLAGS+= -I${.CURDIR}/../btx/lib # Debug me! #CFLAGS+= -g #LDFLAGS+= -g # Pick up ../Makefile.inc early. .include ${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN} btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \ -b ${BTXKERN} ${LOADER}.bin ${LOADER}.bin: ${LOADER}.sym strip -R .comment -R .note -o ${.TARGET} ${.ALLSRC} loader.help: help.common help.i386 cat ${.ALLSRC} | awk -f ${.CURDIR}/../../common/merge_help.awk > ${.TARGET} FILES= ${LOADER} # XXX INSTALLFLAGS_loader= -b FILESMODE_${LOADER}= ${BINMODE} -b .if !defined(LOADER_ONLY) .PATH: ${.CURDIR}/../../forth .include "${.CURDIR}/../../forth/Makefile.inc" FILES+= pcibios.4th FILES+= loader.rc menu.rc .endif # XXX crt0.o needs to be first for pxeboot(8) to work OBJS= ${BTXCRT} DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBGELIBOOT} ${LIBSTAND} LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBGELIBOOT} ${LIBSTAND} .include .if ${MACHINE_CPUARCH} == "amd64" beforedepend ${OBJS}: machine CLEANFILES+= machine CFLAGS+= -DLOADER_PREFER_AMD64 machine: .NOMETA ln -sf ${.CURDIR}/../../../i386/include machine .endif Index: head/sys/boot/i386/loader/chain.c =================================================================== --- head/sys/boot/i386/loader/chain.c (nonexistent) +++ head/sys/boot/i386/loader/chain.c (revision 320011) @@ -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 */ +} Property changes on: head/sys/boot/i386/loader/chain.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/boot/i386/loader/help.i386 =================================================================== --- head/sys/boot/i386/loader/help.i386 (revision 320010) +++ head/sys/boot/i386/loader/help.i386 (revision 320011) @@ -1,45 +1,54 @@ ################################################################################ # Treboot DReboot the system reboot Causes the system to immediately reboot. ################################################################################ # Theap DDisplay memory management statistics heap Requests debugging output from the heap manager. For debugging use only. ################################################################################ # Tset Snum_ide_disks DSet the number of IDE disks NOTE: this variable is deprecated, use root_disk_unit instead. set num_ide_disks= When booting from a SCSI disk on a system with one or more IDE disks, and where the IDE disks are the default boot device, it is necessary to tell the kernel how many IDE disks there are in order to have it correctly locate the SCSI disk you are booting from. ################################################################################ # Tset Sroot_disk_unit DForce the root disk unit number. set root_disk_unit= If the code which detects the disk unit number for the root disk is confused, eg. by a mix of SCSI and IDE disks, or IDE disks with gaps in the sequence (eg. no primary slave), the unit number can be forced by setting this variable. ################################################################################ # Tsmap DDisplay BIOS SMAP table smap 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. + +################################################################################