fastboot.c revision 877400d375b54199aef7c18828cb32f991339ff7
/*
* 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 2008 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.
*
* 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.
*
* 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 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/fastboot.h>
#include <sys/multiboot.h>
/*
* Data structure containing necessary information for the fast reboot
* switcher to jump to the new kernel.
*/
fastboot_info_t newkernel = { 0 };
int fastboot_debug = 0;
int fastboot_contig = 0;
/*
* Fake starting va for new kernel and boot archive.
*/
/*
* Below 1G for page tables as 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;
/* 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 BOOTARCHIVE_FAILSAFE "/boot/x86.miniroot-safe"
static void fastboot_build_pagetables(fastboot_info_t *);
static int fastboot_build_mbi(char *, fastboot_info_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 {
}
return (-1);
}
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 {
}
}
}
/*
* Free up the memory we have allocated for this file
*/
static void
{
int pt_entry_count;
}
/*
* 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
load_kernel(char *mdep)
{
int i;
char kern_bootpath[OBP_MAXPATHLEN];
char bootargs[OBP_MAXPATHLEN];
extern uintptr_t postbootkernelbase;
extern char fb_swtch_image[];
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_BOOTFILE) - 1)) == 0) {
is_failsafe = 1;
}
/*
* Read in unix and boot_archive
*/
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
int page_index;
int pt_entry_count;
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 {
int j;
/* 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.
*/
for (j = 0; j < i; j++)
goto load_kernel_retry;
}
}
if (!fastboot_contig)
== NULL) {
goto err_out;
}
fastboot_filename[i]);
goto err_out;
}
fb->fb_sectcnt = 0;
/*
* Allocate one extra page table entry for terminating
* the list.
*/
if ((fb->fb_pte_list_va =
goto err_out;
}
/*
* 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 (BOOTARCHIVE_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;
}
sizeof (BOOTARCHIVE64));
} else {
goto err_out;
}
} else {
}
}
/*
* Set fb_va to fake_va
*/
for (i = 0; i < FASTBOOT_MAX_FILES_MAP; i++) {
}
/*
* Add the function that will switch us to 32-bit protected mode
*/
/*
* Map in FASTBOOT_SWTCH_PA
*/
/*
* 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 */
MMU_PAGESIZE, 0)) == NULL) {
goto err_out;
}
size - MMU_PAGESIZE;
}
/* Mark it as valid */
return;
}
/*
* Jump to the fast reboot switcher. This function never returns.
*/
void
{
void (*fastboot_func)(fastboot_info_t *);
(*fastboot_func)(&newkernel);
}