iBoot/apps/SecureROM/main.c

372 lines
10 KiB
C

/*
* Copyright (C) 2007-2015 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <arch.h>
#include <debug.h>
#include <platform.h>
#include <platform/memmap.h>
#include <sys.h>
#include <sys/boot.h>
#include <sys/task.h>
#include <lib/blockdev.h>
#include <lib/image.h>
#include <lib/mib_def.h>
#include <drivers/flash_nor.h>
#include <drivers/nvme.h>
#if WITH_USB_DFU
#include <drivers/usb/usb_public.h>
#endif
#if WITH_TBT_DFU
#include <drivers/thunderbolt/thunderboot.h>
#endif
#if WITH_ANC_BOOT
# include <drivers/anc_boot.h>
#endif
#define USER_BUTTON_FORCEDFU_TIMEOUT (6 * 1000 * 1000)
static void boot_selected(enum boot_device boot_device, u_int32_t boot_flag, u_int32_t boot_arg);
static struct image_info *lookup_image_in_bdev(const char *name, uint32_t type);
static bool load_selected_image(struct image_info *loaded_image, u_int32_t type, void *load_address, size_t load_length, u_int32_t boot_flag);
static void boot_loaded_image(void *load_address);
int _main(void)
{
bool force_dfu, force_reset;
utime_t dfu_time;
enum boot_device boot_device;
u_int32_t boot_flag, boot_arg;
int32_t index;
/* initialize the cpu */
arch_cpu_init(false);
dprintf(DEBUG_CRITICAL, "\n" CONFIG_PROGNAME_STRING " start\n");
/* setup the default clock configuration */
dprintf(DEBUG_INFO, "setting up initial clock configuration\n");
platform_init_setup_clocks();
/* set up any internal memory (internal sram) */
dprintf(DEBUG_INFO, "setting up internal memory\n");
platform_init_internal_mem();
/* set up the default pin configuration */
dprintf(DEBUG_INFO, "setting up default pin configuration\n");
platform_init_hwpins();
/* Sample force_dfu and request_dfu pins. */
force_dfu = platform_get_force_dfu();
force_reset = platform_get_request_dfu1() && platform_get_request_dfu2();
/* bring up system services (cpu, tasks, callout, heap) */
sys_init();
/* generate random stack cookie on platforms without a
requirement for fast resume from suspend-to-RAM via SecureROM */
#if !WITH_NO_RANDOM_STACK_COOKIE
sys_init_stack_cookie();
#endif
/* do some early initialization of hardware */
/* timers will start firing at this point */
dprintf(DEBUG_INFO, "doing early platform hardware init\n");
platform_early_init();
/* print version info now that UART is enabled on DEBUG builds */
dprintf(DEBUG_INFO, "\n\n%s for %s\n", CONFIG_PROGNAME_STRING, CONFIG_BOARD_STRING);
dprintf(DEBUG_INFO, "%s\n", build_tag_string);
dprintf(DEBUG_INFO, "%s\n", build_style_string);
/* initialize the rest of hardware */
dprintf(DEBUG_INFO, "doing platform hardware init\n");
platform_init();
/* Check for Force DFU Mode Pin */
dprintf(DEBUG_INFO, "Checking for Force DFU Mode Pin: %x / %x\n", force_dfu, force_reset);
if (force_dfu && !platform_get_usb_cable_connected())
force_dfu = false;
/* Check for Force DFU Mode request pins. Requesting DFU mode is done
by asserting both REQUEST_DFU1 and REQUEST_DFU2, holding them for
the required time, then deasserting REQUEST_DFU1, followed by
deasserting REQUEST_DFU2 after another required hold time.
*/
dprintf(DEBUG_INFO, "Checking for Force DFU Mode request pins: %x / %x\n", force_dfu, force_reset);
if (!force_dfu && force_reset && platform_get_usb_cable_connected()) {
dfu_time = system_time();
while (platform_get_request_dfu1() && platform_get_request_dfu2() && platform_get_usb_cable_connected()) {
if (time_has_elapsed(dfu_time, USER_BUTTON_FORCEDFU_TIMEOUT))
break;
task_sleep(100 * 1000);
}
dfu_time = system_time();
while (!platform_get_request_dfu1() && platform_get_request_dfu2() && platform_get_usb_cable_connected()) {
if (time_has_elapsed(dfu_time, USER_BUTTON_FORCEDFU_TIMEOUT)) {
force_dfu = true;
break;
}
task_sleep(100 * 1000);
}
}
/* Try the boot devices in order */
index = 0;
/* if DFU is being forced, set index to -1 for force dfu */
dprintf(DEBUG_INFO, "Force DFU: %x\n", force_dfu);
if (force_dfu) index = -1;
while (1) {
/* ask the platform code for the selected boot source */
if (!platform_get_boot_device(index, &boot_device, &boot_flag, &boot_arg))
break;
/* Attempt to boot the selected device */
boot_selected(boot_device, boot_flag, boot_arg);
if (index < 0)
continue;
index++;
}
/* we are hosed */
dprintf(DEBUG_INFO, "No valid boot device, resetting.\n");
platform_reset(false);
}
/*
* Attempt to boot the seleced device.
*/
static void
boot_selected(enum boot_device boot_device, uint32_t boot_flag, uint32_t boot_arg)
{
struct image_info *loaded_image;
size_t loaded_length;
void *load_address;
size_t load_length;
bool loaded = false;
uint32_t type = IMAGE_TYPE_LLB;
dprintf(DEBUG_INFO, "boot_selected: boot_device: %08x, boot_flag: %08x, boot_arg: %08x\n",
boot_device, boot_flag, boot_arg);
/* Enable the pins for the selected boot device */
platform_enable_boot_interface(true, boot_device, boot_arg);
/* reset security and clear the insecure memory area */
security_init(true);
loaded_image = NULL;
loaded_length = 0;
load_address = (void *)INSECURE_MEMORY_BASE;
load_length = INSECURE_MEMORY_SIZE;
/* ask the boot source to fetch the boot image */
switch (boot_device) {
#if WITH_HW_FLASH_NOR && WITH_HW_FLASH_NOR_SPI
case BOOT_DEVICE_SPI:
{
dprintf(DEBUG_INFO, "SPI NOR boot selected\n");
/* initialise the NOR flash for the desired channel */
flash_nor_init(boot_arg);
loaded_image = lookup_image_in_bdev("nor0", type);
if (loaded_image != NULL)
loaded_length = loaded_image->imageLength;
break;
}
#endif /* WITH_HW_FLASH_NOR && WITH_HW_FLASH_NOR_SPI */
#if WITH_ANC_BOOT
case BOOT_DEVICE_NAND:
{
if (!anc_reset(boot_arg)) {
dprintf(DEBUG_CRITICAL, "Failed to init ASP");
break;
}
loaded_length = anc_read_llb(load_address, load_length);
if (loaded_length > load_length)
panic("load overflow");
if (loaded_length > 0) {
if ((loaded_image = image_create_from_memory(
load_address,
loaded_length,
IMAGE_OPTION_LOCAL_STORAGE)) == NULL) {
dprintf(DEBUG_INFO, "image creating failed\n");
} else {
dprintf(DEBUG_INFO, "loaded image from ANC\n");
}
}
break;
}
#endif
#if WITH_NVME
case BOOT_DEVICE_NVME:
{
loaded_image = lookup_image_in_bdev("nvme_firmware0", type);
if (loaded_image != NULL)
loaded_length = loaded_image->imageLength;
break;
}
#endif
#if WITH_USB_DFU
case BOOT_DEVICE_USBDFU:
{
int result;
/* Turn on the DFU STATUS pin */
platform_set_dfu_status(true);
/* Set type to iBSS */
type = IMAGE_TYPE_IBSS;
/* try to get an image via DFU */
if ((result = getDFUImage(load_address, load_length)) < 0) {
dprintf(DEBUG_INFO, "fatal DFU download error");
break;
}
loaded_length = (size_t)result;
if (loaded_length > load_length)
panic("load overflow");
/* wrap the image */
if ((loaded_image = image_create_from_memory(load_address, loaded_length, 0)) == NULL) {
dprintf(DEBUG_INFO, "failed to create image from DFU download\n");
break;
}
dprintf(DEBUG_INFO, "loaded image from USB-DFU\n");
break;
}
#endif /* WITH_USB_DFU */
#if WITH_TBT_DFU
case BOOT_DEVICE_TBTDFU:
{
int result;
/* Set type to iBSS */
type = IMAGE_TYPE_IBSS;
/* try to get an image via DFU */
if ((result = thunderboot_get_dfu_image(load_address, load_length)) < 0) {
dprintf(DEBUG_INFO, "fatal DFU download error");
break;
}
loaded_length = (size_t)result;
if (loaded_length > load_length)
panic("load overflow");
/* wrap the image */
if ((loaded_image = image_create_from_memory(load_address, loaded_length, 0)) == NULL) {
dprintf(DEBUG_INFO, "failed to create image from DFU download\n");
break;
}
dprintf(DEBUG_INFO, "loaded image from TBT-DFU\n");
break;
}
#endif /* WITH_USB_DFU */
/* case BOOT_DEVICE_XMODEM: */
default:
break;
}
/* if we found an image, try to load it */
if (loaded_image) {
loaded = load_selected_image(loaded_image, type, load_address, loaded_length, boot_flag);
image_free(loaded_image);
} else {
loaded = false;
}
/* Disable the pins for the selected boot device */
platform_enable_boot_interface(false, boot_device, boot_arg);
if (loaded)
boot_loaded_image(load_address);
/* load failed or boot source not supported */
dprintf(DEBUG_INFO, "failed to load from selected boot device\n");
}
static struct image_info *
lookup_image_in_bdev(const char *name, uint32_t type)
{
struct blockdev *boot_bdev;
struct image_info *loaded_image;
/* look it up */
if ((boot_bdev = lookup_blockdev(name)) == NULL)
return NULL;
dprintf(DEBUG_INFO, "boot device %s initialised\n", name);
/* look for the image directory in the expected location */
if (image_search_bdev(boot_bdev, 0, IMAGE_OPTION_LOCAL_STORAGE) == 0)
return NULL;
dprintf(DEBUG_INFO, "found image directory on %s\n", name);
/* look for the LLB */
loaded_image = image_find(type);
dprintf(DEBUG_INFO, "%s LLB image on %s\n", loaded_image != NULL ? "found" : "failed to find", name);
return loaded_image;
}
static bool
load_selected_image(struct image_info *loaded_image, u_int32_t type, void *load_address, size_t load_length, u_int32_t boot_flag)
{
/* image epoch must be greater than or equal to the SecureROM's epoch */
loaded_image->imageOptions |= IMAGE_OPTION_GREATER_EPOCH;
/* image decode library make use of this information to do various validation on image */
loaded_image->imageOptions |= IMAGE_OPTION_NEW_TRUST_CHAIN;
/* if test mode isn't set, we require the image be trusted, regardless of the security fuse */
if (!(boot_flag & BOOT_FLAG_TEST_MODE))
loaded_image->imageOptions |= IMAGE_OPTION_REQUIRE_TRUST;
/* validate the boot image */
if (image_load(loaded_image, &type, 1, NULL, &load_address, &load_length)) {
dprintf(DEBUG_INFO, "image load failed\n");
return false;
}
return true;
}
static void
boot_loaded_image(void *load_address)
{
/* consolidate environment post image validation */
security_consolidate_environment();
#if WITH_SIDP
/* Seal the ROM manifest for Silicon Data Protection */
security_sidp_seal_rom_manifest();
#endif
dprintf(DEBUG_CRITICAL, "executing image...\n");
prepare_and_jump(BOOT_UNKNOWN, (void *)load_address, NULL);
}