fastboot.c revision 753a6d457b330b1b29b2d3eefcd0831116ce950d
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file contains the functions for performing Fast Reboot -- a
* reboot which bypasses the firmware and bootloader, considerably
* reducing downtime.
*
* fastboot_load_kernel(): This function is invoked by mdpreboot() in the
* reboot path. It loads the new kernel and boot archive into memory, builds
* the data structure containing sufficient information about the new
* kernel and boot archive to be passed to the fast reboot switcher
* (see fb_swtch_src.s for details). When invoked the switcher relocates
* the new kernel and boot archive to physically contiguous low memory,
* similar to where the boot loader would have loaded them, and jumps to
* the new kernel.
*
* If fastreboot_onpanic is enabled, fastboot_load_kernel() is called
* by fastreboot_post_startup() to load the back up kernel in case of
* panic.
*
* The physical addresses of the memory allocated for the new kernel, boot
* archive and their page tables must be above where the boot archive ends
* after it has been relocated by the switcher, otherwise the new files
* and their page tables could be overridden during relocation.
*
* fast_reboot(): This function is invoked by mdboot() once it's determined
* that the system is capable of fast reboot. It jumps to the fast reboot
* switcher with the data structure built by fastboot_load_kernel() as the
* argument.
*/
#include <sys/segments.h>
#include <sys/sysmacros.h>
#include <sys/bootconf.h>
#include <sys/ddidmareq.h>
#include <sys/vm_machparam.h>
#include <sys/archsystm.h>
#include <sys/machsystm.h>
#include <sys/x86_archext.h>
#include <sys/smp_impldefs.h>
#include <sys/fastboot.h>
#include <sys/multiboot.h>
#include <sys/kobj_lex.h>
/*
* Macro to determine how many pages are needed for PTEs to map a particular
* file. Allocate one extra page table entry for terminating the list.
*/
#define FASTBOOT_PTE_LIST_SIZE(fsize) \
/*
* Data structure containing necessary information for the fast reboot
* switcher to jump to the new kernel.
*/
fastboot_info_t newkernel = { 0 };
char fastboot_args[OBP_MAXPATHLEN];
int fastboot_debug = 0;
int fastboot_contig = 0;
/*
* Fake starting va for new kernel and boot archive.
*/
/*
* Reserve memory below PA 1G in preparation of fast reboot.
*
* This variable is only checked when fastreboot_capable is set, but
* fastreboot_onpanic is not set. The amount of memory reserved
* is negligible, but just in case we are really short of low memory,
* this variable will give us a backdoor to not consume memory at all.
*/
int reserve_mem_enabled = 1;
/*
* Amount of memory below PA 1G to reserve for constructing the multiboot
* data structure and the page tables as we tend to run out of those
* when more drivers are loaded.
*/
/*
* Use below 1G for page tables as
* 1. we are only doing 1:1 mapping of the bottom 1G of physical memory.
* 2. we are using 2G as the fake virtual address for the new kernel and
* boot archive.
*/
static ddi_dma_attr_t fastboot_below_1G_dma_attr = {
0x0000000008000000ULL, /* dma_attr_addr_lo: 128MB */
0x000000003FFFFFFFULL, /* dma_attr_addr_hi: 1G */
0x00000000FFFFFFFFULL, /* dma_attr_count_max */
0x0000000000001000ULL, /* dma_attr_align: 4KB */
1, /* dma_attr_burstsize */
1, /* dma_attr_minxfer */
0x00000000FFFFFFFFULL, /* dma_attr_maxxfer */
0x00000000FFFFFFFFULL, /* dma_attr_seg */
1, /* dma_attr_sgllen */
0x1000ULL, /* dma_attr_granular */
0, /* dma_attr_flags */
};
static ddi_dma_attr_t fastboot_dma_attr = {
0x0000000008000000ULL, /* dma_attr_addr_lo: 128MB */
#ifdef __amd64
0xFFFFFFFFFFFFFFFFULL, /* dma_attr_addr_hi: 2^64B */
#else
0x0000000FFFFFFFFFULL, /* dma_attr_addr_hi: 64GB */
#endif /* __amd64 */
0x00000000FFFFFFFFULL, /* dma_attr_count_max */
0x0000000000001000ULL, /* dma_attr_align: 4KB */
1, /* dma_attr_burstsize */
1, /* dma_attr_minxfer */
0x00000000FFFFFFFFULL, /* dma_attr_maxxfer */
0x00000000FFFFFFFFULL, /* dma_attr_seg */
1, /* dma_attr_sgllen */
0x1000ULL, /* dma_attr_granular */
0, /* dma_attr_flags */
};
/*
* Various information saved from the previous boot to reconstruct
* multiboot_info.
*/
extern multiboot_info_t saved_mbi;
extern char saved_cmdline[FASTBOOT_SAVED_CMDLINE_LEN];
extern int saved_cmdline_len;
extern size_t saved_file_size[];
/* PRINTLIKE */
/*
* Need to be able to get boot_archives from other places
*/
#define BOOTARCHIVE64 "/platform/i86pc/amd64/boot_archive"
#define BOOTARCHIVE32 "/platform/i86pc/boot_archive"
#define BOOTARCHIVE32_FAILSAFE "/boot/x86.miniroot-safe"
#define BOOTARCHIVE64_FAILSAFE "/boot/amd64/x86.miniroot-safe"
static void fastboot_build_pagetables(fastboot_info_t *);
static int fastboot_build_mbi(char *, fastboot_info_t *);
static void fastboot_free_file(fastboot_file_t *);
static const char fastboot_enomem_msg[] = "Fastboot: Couldn't allocate 0x%"
PRIx64" bytes below %s to do fast reboot";
static void
{
if (!fastboot_debug)
return;
}
/*
* Return the index corresponding to a virt address at a given page table level.
*/
static uint_t
{
}
/*
* Add mapping from vstart to pstart for the specified size.
* vstart, pstart and size should all have been aligned at 2M boundaries.
*/
static void
{
int index, l;
if (l == level) {
/*
* Last level. Program the page table entries.
*/
if (l > 0)
else
}
} else {
/*
* Intermediate levels.
* Program with either valid bit or PTP bits.
*/
if (l == nk->fi_top_level) {
#ifdef __amd64
#else
#endif /* __amd64 */
} else {
}
}
}
}
/*
* Build page tables for the lower 1G of physical memory using 2M
* pages, and prepare page tables for mapping new kernel and boot
* archive pages using 4K pages.
*/
static void
{
/*
* Map lower 1G physical memory. Use large pages.
*/
/*
* Map one 4K page to get the middle page tables set up.
*/
}
/*
* Sanity check. Look for dboot offset.
*/
static int
{
int i;
return (-1);
return (0);
}
}
}
return (-1);
}
/*
* Initialize text and data section information for 32-bit kernel.
* On entry, *sectcntp contains maximum allowable number of sections;
* on return, it contains the actual number of sections filled.
*/
static int
{
int i;
int used_sections = 0;
const int max_sectcnt = *sectcntp;
return (-1);
return (-1);
continue;
} else {
if (max_sectcnt <= used_sections)
return (-1);
/* Extra sanity check for the input object file */
return (-1);
}
}
return (0);
}
/*
* Create multiboot info structure
*/
static int
{
char bootargs[OBP_MAXPATHLEN];
} else {
}
nk->fi_mbi_size = 0;
}
if (nk->fi_mbi_size == 0) {
if ((nk->fi_new_mbi_va =
return (-1);
}
/*
* fi_mbi_size must be set after the allocation succeeds
* as it's used to determine how much memory to free.
*/
}
/*
* Map the address into both the current proc's address
* space and the kernel's address space in case the panic
* is forced by kmdb.
*/
}
next_addr += sizeof (mb_module_t);
*(char *)next_addr = '\0';
next_addr++;
} else {
}
/* Terminate the string */
return (0);
}
/*
* Initialize HAT related fields
*/
static void
{
if (x86_feature & X86_PAE) {
#ifdef __amd64
#else
#endif /* __amd64 */
}
}
/*
* Process boot argument
*/
static void
char *bootargs)
{
int i;
/*
* If mdep is not NULL, it comes in the format of
* mountpoint unix args
*/
if (mdep[0] != '-') {
/* First get the root argument */
i = 0;
i++;
}
/* mount point */
kern_bootpath[i] = '\0';
*bootpath_len = i;
/*
* Get the next argument. It should be unix as
* we have validated in in halt.c.
*/
mdep += (i + 1);
i = 0;
while (mdep[i] != '\0' &&
mdep[i] != ' ') {
i++;
}
}
}
kern_bootfile[i] = '\0';
} else {
}
}
}
/*
* Reserve memory under PA 1G for mapping the new kernel and boot archive.
* This function is only called if fastreboot_onpanic is *not* set.
*/
static void
{
int i;
/*
* A valid kernel is in place. No need to reserve any memory.
*/
return;
/*
* Reserve memory under PA 1G for PTE lists.
*/
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
return;
}
}
/*
* Reserve memory under PA 1G for page tables.
*/
if ((nk->fi_pagetable_va =
return;
}
/*
* Reserve memory under PA 1G for multiboot structure.
*/
return;
}
}
/*
* Calculate MD5 digest for the given fastboot_file.
* Assumes that the file is allready loaded properly.
*/
static void
{
}
/*
* Free up the memory we have allocated for a file
*/
static void
{
if (fsize_roundup) {
}
}
/*
* Free up memory used by the PTEs for a file.
*/
static void
{
fb->fb_pte_list_va = 0;
fb->fb_pte_list_pa = 0;
fb->fb_pte_list_size = 0;
}
}
/*
* Free up all the memory used for representing a kernel with
* fastboot_info_t.
*/
static void
{
int i;
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
}
nk->fi_pagetable_va = 0;
nk->fi_pagetable_pa = 0;
nk->fi_pagetable_size = 0;
}
nk->fi_new_mbi_va = 0;
nk->fi_new_mbi_pa = 0;
nk->fi_mbi_size = 0;
}
}
/*
* Only free up the memory allocated for the kernel and boot archive,
* but not for the page tables.
*/
void
{
int i;
/*
* Free the memory we have allocated
*/
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
}
}
static void
{
int i;
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
}
}
/*
* Generate MD5 checksum of the given kernel.
*/
static void
{
int i;
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
}
}
/*
* Calculate MD5 checksum of the given kernel and verify that
* it matches with what was calculated before.
*/
int
{
int i;
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
sizeof (nk->fi_md5_hash[i])) != 0)
return (i + 1);
}
sizeof (nk->fi_md5_hash[i])) != 0)
return (i + 1);
return (0);
}
/*
* This function performs the following tasks:
* - Read the sizes of the new kernel and boot archive.
* - Allocate memory for the new kernel and boot archive.
* - Allocate memory for page tables necessary for mapping the memory
* allocated for the files.
* - Read the new kernel and boot archive into memory.
* - Map in the fast reboot switcher.
* - Load the fast reboot switcher to FASTBOOT_SWTCH_PA.
* - Build the new multiboot_info structure
* - Build page tables for the low 1G of physical memory.
* - Mark the data structure as valid if all steps have succeeded.
*/
void
fastboot_load_kernel(char *mdep)
{
int i;
char kern_bootpath[OBP_MAXPATHLEN];
extern uintptr_t postbootkernelbase;
int bootpath_len = 0;
int is_failsafe = 0;
int is_retry = 0;
postbootkernelbase = 0;
/*
* Initialize various HAT related fields in the data structure
*/
/*
* Process the boot argument
*/
/*
* Make sure we get the null character
*/
(sizeof (FAILSAFE_BOOTFILE32) - 1)) == 0 ||
(sizeof (FAILSAFE_BOOTFILE64) - 1)) == 0) {
is_failsafe = 1;
}
/*
* Read in unix and boot_archive
*/
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
int page_index;
dprintf("fastboot_filename[%d] = %s\n",
i, fastboot_filename[i]);
(struct _buf *)-1) {
fastboot_filename[i]);
goto err_out;
}
"Fastboot: Couldn't get filesize for %s",
fastboot_filename[i]);
goto err_out;
}
/*
* Where the files end in physical memory after being
* relocated by the fast boot switcher.
*/
goto err_out;
}
/*
* Adjust dma_attr_addr_lo so that the new kernel and boot
* archive will not be overridden during relocation.
*/
if (is_retry) {
/*
* If we have already tried and didn't succeed,
* just give up.
*/
"Fastboot: boot archive is too big");
goto err_out;
} else {
/* Set the flag so we don't keep retrying */
is_retry++;
/* Adjust dma_attr_addr_lo */
/*
* Free the memory we have already allocated
* whose physical addresses might not fit
* the new lo and hi constraints.
*/
goto load_kernel_retry;
}
}
if (!fastboot_contig)
== NULL) {
goto err_out;
}
fastboot_filename[i]);
goto err_out;
}
fb->fb_sectcnt = 0;
/*
* If we have reserved memory but it not enough, free it.
*/
fb->fb_pte_list_size = 0;
}
if (fb->fb_pte_list_size == 0) {
if ((fb->fb_pte_list_va =
== NULL) {
goto err_out;
}
/*
* fb_pte_list_size must be set after the allocation
* succeeds as it's used to determine how much memory to
* free.
*/
}
/*
* Include the pte_bits so we don't have to make
* it in assembly.
*/
}
if (i == FASTBOOT_UNIX) {
int j;
/*
* Sanity checks:
*/
for (j = 0; j < SELFMAG; j++) {
"signature");
goto err_out;
}
}
sizeof (fb->fb_sections[0]);
if (fastboot_elf32_find_loadables((void *)va,
"program section failure");
goto err_out;
}
if (fb->fb_sectcnt == 0) {
"program sections found");
goto err_out;
}
if (is_failsafe) {
/* Failsafe boot_archive */
[bootpath_len],
sizeof (BOOTARCHIVE32_FAILSAFE));
} else {
[bootpath_len],
sizeof (BOOTARCHIVE32));
}
!= 0) {
"find ELF64 dboot entry offset");
goto err_out;
}
if ((x86_feature & X86_64) == 0 ||
(x86_feature & X86_PAE) == 0) {
"reboot to %s: "
"not a 64-bit capable system",
goto err_out;
}
if (is_failsafe) {
/* Failsafe boot_archive */
[bootpath_len],
sizeof (BOOTARCHIVE64_FAILSAFE));
} else {
[bootpath_len],
sizeof (BOOTARCHIVE64));
}
} else {
goto err_out;
}
} else {
}
}
/*
* Add the function that will switch us to 32-bit protected mode
*/
/*
* Build the new multiboot_info structure
*/
goto err_out;
}
/*
* Build page table for low 1G physical memory. Use big pages.
* Allocate 4 (5 for amd64) pages for the page tables.
* 1 page for PML4 (amd64)
* 1 page for Page-Directory-Pointer Table
* 2 pages for Page Directory
* 1 page for Page Table.
* The page table entry will be rewritten to map the physical
* address as we do the copying.
*/
if (newkernel.fi_has_pae) {
#ifdef __amd64
#else
#endif /* __amd64 */
< size) {
}
if (newkernel.fi_pagetable_size == 0) {
MMU_PAGESIZE, 0)) == NULL) {
goto err_out;
}
/*
* fi_pagetable_size must be set after the allocation
* succeeds as it's used to determine how much memory to
* free.
*/
}
size - MMU_PAGESIZE;
}
/* Generate MD5 checksums */
/* Mark it as valid */
return;
}
/* ARGSUSED */
static int
{
void (*fastboot_func)(fastboot_info_t *);
}
/*
* If we have pinned a thread, make sure the address is mapped
* in the address space of the pinned thread.
*/
(*fastboot_func)(nk);
/*NOTREACHED*/
return (0);
}
/*
* Jump to the fast reboot switcher. This function never returns.
*/
void
{
processorid_t bootcpuid = 0;
extern uintptr_t postbootkernelbase;
extern char fb_swtch_image[];
int i;
postbootkernelbase = 0;
/*
* Map the address into both the current proc's address
* space and the kernel's address space in case the panic
* is forced by kmdb.
*/
}
/*
* Set fb_va to fake_va
*/
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
}
/* Do what panic_idle() does */
for (;;)
;
} else
(void) fastboot_xc_func(&newkernel, 0, 0);
}
/*
* Get boot property value for fastreboot_onpanic.
*
* new setting passed in via "-B fastreboot_onpanic" is ignored.
* This order of precedence is to enable developers debugging panics
* that occur early in boot to utilize Fast Reboot on panic.
*/
static void
fastboot_get_bootprop(void)
{
devi = ddi_root_node();
if (ret == DDI_PROP_SUCCESS) {
val = 0;
else if (FASTREBOOT_ONPANIC_ISSET(propstr))
/*
* Only set fastreboot_onpanic to the value passed in
* if it's not already set to non-zero, and the value
* has indeed been passed in via command line.
*/
}
len = sizeof (fastreboot_onpanic_cmdline);
if (ret == DDI_PROP_BUF_TOO_SMALL)
}
/*
* This function is called by main() to either load the backup kernel for panic
* fast reboot, or to reserve low physical memory for fast reboot.
*/
void
{
if (!fastreboot_capable)
return;
if (fastreboot_onpanic)
else if (reserve_mem_enabled)
}
/*
* Update boot configuration settings.
* If the new fastreboot_onpanic setting is false, and a kernel has
* been preloaded, free the memory;
* if the new fastreboot_onpanic setting is true and newkernel is
* not valid, load the new kernel.
*/
void
fastboot_update_config(const char *mdep)
{
if (!fastreboot_capable)
return;
if (fastreboot_onpanic && (!cur_fastreboot_onpanic ||
if (cur_fastreboot_onpanic && !fastreboot_onpanic)
}