first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.
|
||||
*/
|
||||
|
||||
// Taken from pexpert/pexpert/arm/boot.h and pexpert/pexpert/arm64/boot.h
|
||||
// Modified for iBoot:
|
||||
// - Fixed incosistent naming in boot-args struct
|
||||
// - Added monitor_boot_args
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007-2009 Apple Inc. All rights reserved.
|
||||
* Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* @OSF_COPYRIGHT@
|
||||
*/
|
||||
|
||||
#ifndef _PEXPERT_ARM_BOOT_H_
|
||||
#define _PEXPERT_ARM_BOOT_H_
|
||||
|
||||
//#include <kern/kern_types.h>
|
||||
|
||||
#define BOOT_LINE_LENGTH 256
|
||||
|
||||
/*
|
||||
* Video information..
|
||||
*/
|
||||
|
||||
struct boot_video {
|
||||
unsigned long v_baseAddr; /* Base address of video memory */
|
||||
unsigned long v_display; /* Display Code (if Applicable */
|
||||
unsigned long v_rowBytes; /* Number of bytes per pixel row */
|
||||
unsigned long v_width; /* Width */
|
||||
unsigned long v_height; /* Height */
|
||||
unsigned long v_depth; /* Pixel Depth and other parameters */
|
||||
};
|
||||
|
||||
#define kBootVideoDepthMask (0xFF)
|
||||
#define kBootVideoDepthDepthShift (0)
|
||||
#define kBootVideoDepthRotateShift (8)
|
||||
#define kBootVideoDepthScaleShift (16)
|
||||
|
||||
typedef struct boot_video boot_video;
|
||||
|
||||
/* Boot argument structure - passed into Mach kernel at boot time.
|
||||
*/
|
||||
#define kBootArgsRevision 2
|
||||
#define kBootArgsVersion1 1
|
||||
#define kBootArgsVersion2 2
|
||||
|
||||
#define kBootFlagDarkBoot 1
|
||||
|
||||
#if defined(__LP64__)
|
||||
typedef struct boot_args {
|
||||
uint16_t revision; /* Revision of boot_args structure */
|
||||
uint16_t version; /* Version of boot_args structure */
|
||||
uint64_t virtBase; /* Virtual base of memory */
|
||||
uint64_t physBase; /* Physical base of memory */
|
||||
uint64_t memSize; /* Size of memory */
|
||||
uint64_t topOfKernelData; /* Highest physical address used in kernel data area */
|
||||
boot_video video; /* Video Information */
|
||||
uint32_t machineType; /* Machine Type */
|
||||
void *deviceTreeP; /* Base of flattened device tree */
|
||||
uint32_t deviceTreeLength; /* Length of flattened tree */
|
||||
char commandLine[BOOT_LINE_LENGTH]; /* Passed in command line */
|
||||
uint64_t boot_flags; /* Misc boot flags*/
|
||||
} boot_args;
|
||||
#else
|
||||
typedef struct boot_args {
|
||||
uint16_t revision; /* Revision of boot_args structure */
|
||||
uint16_t version; /* Version of boot_args structure */
|
||||
uint32_t virtBase; /* Virtual base of memory */
|
||||
uint32_t physBase; /* Physical base of memory */
|
||||
uint32_t memSize; /* Size of memory */
|
||||
uint32_t topOfKernelData; /* Highest physical address used in kernel data area */
|
||||
boot_video video; /* Video Information */
|
||||
uint32_t machineType; /* Machine Type */
|
||||
void *deviceTreeP; /* Base of flattened device tree */
|
||||
uint32_t deviceTreeLength; /* Length of flattened tree */
|
||||
char commandLine[BOOT_LINE_LENGTH]; /* Passed in command line */
|
||||
uint32_t boot_flags; /* Misc boot flags*/
|
||||
} boot_args;
|
||||
#endif
|
||||
|
||||
#define SOC_DEVICE_TYPE_BUFFER_SIZE 32
|
||||
|
||||
#define PC_TRACE_BUF_SIZE 1024
|
||||
|
||||
/*
|
||||
* Monitor/hypervisor boot argument structure.
|
||||
*/
|
||||
#if defined(__LP64__)
|
||||
typedef struct monitor_boot_args {
|
||||
uint64_t version; /* structure version - this is version 2 */
|
||||
uint64_t virtBase; /* virtual base of memory assigned to the monitor */
|
||||
uint64_t physBase; /* physical address corresponding to the virtual base */
|
||||
uint64_t memSize; /* size of memory assigned to the monitor */
|
||||
uint64_t kernArgs; /* physical address of the kernel boot_args structure */
|
||||
uint64_t kernEntry; /* kernel entrypoint */
|
||||
uint64_t kernPhysBase; /* physical base of the kernel's address space */
|
||||
uint64_t kernPhysSlide; /* offset from kernPhysBase to kernel load address */
|
||||
uint64_t kernVirtSlide; /* virtual slide applied to kernel at load time */
|
||||
} monitor_boot_args;
|
||||
#else
|
||||
typedef struct monitor_boot_args {
|
||||
uint32_t version; /* structure version - this is version 2 */
|
||||
uint32_t virtBase; /* virtual base of memory assigned to the monitor */
|
||||
uint32_t physBase; /* physical address corresponding to the virtual base */
|
||||
uint32_t memSize; /* size of memory assigned to the monitor */
|
||||
uint32_t kernArgs; /* physical address of the kernel boot_args structure */
|
||||
uint32_t kernEntry; /* kernel entrypoint */
|
||||
uint32_t kernPhysBase; /* physical base of the kernel's address space */
|
||||
uint32_t kernPhysSlide; /* offset from kernPhysBase to kernel load address */
|
||||
uint32_t kernVirtSlide; /* virtual slide applied to kernel at load time */
|
||||
} monitor_boot_args;
|
||||
#endif
|
||||
|
||||
#endif /* _PEXPERT_ARM_BOOT_H_ */
|
||||
+874
@@ -0,0 +1,874 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2014 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 1998-2006 Apple Computer, 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 <debug.h>
|
||||
#if WITH_HW_GASGAUGE
|
||||
#include <drivers/gasgauge.h>
|
||||
#endif
|
||||
#include <drivers/power.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include <lib/env.h>
|
||||
#include <lib/image.h>
|
||||
#include <lib/macho.h>
|
||||
#include <lib/net.h>
|
||||
#include <lib/nvram.h>
|
||||
#include <lib/paint.h>
|
||||
#include <lib/random.h>
|
||||
#include <lib/syscfg.h>
|
||||
#if WITH_TICKET
|
||||
#include <lib/ticket.h>
|
||||
#endif
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <sys.h>
|
||||
#include <sys/hash.h>
|
||||
#include <target.h>
|
||||
#if WITH_CONSISTENT_DBG
|
||||
#include <drivers/consistent_debug.h>
|
||||
#endif
|
||||
|
||||
extern bool contains_boot_arg(const char *bootArgs, const char *arg, bool prefixmatch);
|
||||
|
||||
#include "boot.h"
|
||||
|
||||
#define kBootPResponceSize (0x12C)
|
||||
static char gBootPResponse[kBootPResponceSize];
|
||||
|
||||
#define ROOT_DTPATH ""
|
||||
|
||||
#ifndef ETHERNET_DTPATH
|
||||
#define ETHERNET_DTPATH "ethernet"
|
||||
#endif
|
||||
|
||||
#ifndef MULTITOUCH_DTPATH
|
||||
#define MULTITOUCH_DTPATH ""
|
||||
#endif
|
||||
|
||||
#ifndef WIFI_DTPATH
|
||||
#define WIFI_DTPATH "arm-io/sdio"
|
||||
#endif
|
||||
|
||||
#ifndef PINTO_DTPATH
|
||||
#define PINTO_DTPATH ""
|
||||
#endif
|
||||
|
||||
#ifndef BT_DTPATH
|
||||
#define BT_DTPATH "arm-io/uart3/bluetooth"
|
||||
#endif
|
||||
#ifndef BASEBAND_DTPATH
|
||||
#define BASEBAND_DTPATH "baseband"
|
||||
#endif
|
||||
|
||||
#ifndef CHARGER_DTPATH
|
||||
#define CHARGER_DTPATH "charger"
|
||||
#endif
|
||||
|
||||
#ifndef USBDEVICE_DTPATH
|
||||
#define USBDEVICE_DTPATH "arm-io/usb-complex/usb-device"
|
||||
#endif
|
||||
|
||||
int UpdateDeviceTree(const boot_args *boot_args)
|
||||
{
|
||||
DTNodePtr node, chosen;
|
||||
uint32_t propSize, boot_partition;
|
||||
char *propName;
|
||||
void *propData;
|
||||
const char *str;
|
||||
int result, noenet = 0;
|
||||
uint32_t ipaddr;
|
||||
uint8_t ethaddr[6];
|
||||
void *wifiaddr;
|
||||
const char *dram_vendor_string;
|
||||
const char *product_id;
|
||||
#if WITH_SYSCFG
|
||||
void *batteryId = NULL;
|
||||
uint32_t batteryIdSize = 0;
|
||||
#endif
|
||||
|
||||
#if WITH_SYSCFG
|
||||
// Put the model number in the root node
|
||||
if (FindNode(0, ROOT_DTPATH, &node)) {
|
||||
propName = "model-number";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('Mod#', propData, propSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Put the regulatory model number in the root node
|
||||
if (FindNode(0, ROOT_DTPATH, &node)) {
|
||||
propName = "regulatory-model-number";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
if (syscfgCopyDataForTag('RMd#', propData, propSize) == -1 && ((char *)propData)[0] == 0)
|
||||
propName[0] = '~';
|
||||
}
|
||||
|
||||
// Put the region info in the root node
|
||||
if (FindNode(0, ROOT_DTPATH, &node)) {
|
||||
propName = "region-info";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('Regn', propData, propSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Put the serial number in the root node
|
||||
if (FindNode(0, ROOT_DTPATH, &node)) {
|
||||
propName = "serial-number";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('SrNm', propData, propSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Put the mlb serial number in the root node
|
||||
if (FindNode(0, ROOT_DTPATH, &node)) {
|
||||
propName = "mlb-serial-number";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('MLB#', propData, propSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Put the config number in the root node
|
||||
if (FindNode(0, ROOT_DTPATH, &node)) {
|
||||
propName = "config-number";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('CFG#', propData, propSize);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Find the pram node
|
||||
if (FindNode(0, "pram", &node)) {
|
||||
// Fill in the memory range
|
||||
propName = "reg";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
((uintptr_t *)propData)[0] = platform_get_memory_region_base(kMemoryRegion_Panic);
|
||||
((uintptr_t *)propData)[1] = platform_get_memory_region_size(kMemoryRegion_Panic);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the vram node
|
||||
if (FindNode(0, "vram", &node)) {
|
||||
// Fill in the memory range
|
||||
propName = "reg";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
((uintptr_t *)propData)[0] = platform_get_memory_region_base(kMemoryRegion_Display);
|
||||
((uintptr_t *)propData)[1] = platform_get_memory_region_size(kMemoryRegion_Display);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(SLEEP_TOKEN_BUFFER_BASE) && defined(SLEEP_TOKEN_BUFFER_SIZE)
|
||||
// Find the stram (sleep-token ram) node
|
||||
if (FindNode(0, "stram", &node)) {
|
||||
// Fill in the memory range
|
||||
propName = "reg";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
((uintptr_t *)propData)[0] = platform_get_memory_region_base(kMemoryRegion_SleepToken);
|
||||
((uintptr_t *)propData)[1] = platform_get_memory_region_size(kMemoryRegion_SleepToken);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Find the arm-io node
|
||||
if (FindNode(0, "arm-io", &node)) {
|
||||
dt_set_prop_32(node, "chip-revision", platform_get_chip_revision());
|
||||
dt_set_prop_32(node, "fuse-revision", platform_get_fuse_revision());
|
||||
}
|
||||
|
||||
if (FindNode(0, "product", &node)) {
|
||||
propName = "product-id";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
product_id = platform_get_product_id();
|
||||
memcpy(propData, product_id, propSize);
|
||||
}
|
||||
|
||||
#if WITH_SYSCFG
|
||||
propName = "device-color-legacy";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('ClrC', propData, propSize);
|
||||
}
|
||||
|
||||
propName = "primary-calibration-matrix";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('DPCl', propData, propSize);
|
||||
|
||||
uint32_t *data_ptr = (uint32_t*)propData;
|
||||
if (data_ptr[0] == 0x01000000) {
|
||||
for(uint32_t i = 0; i < ((uint32_t)propSize / sizeof(uint32_t)); i ++) {
|
||||
data_ptr[i] = swap32(data_ptr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_PAINT
|
||||
propName = "device-colors";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
paint_color_map_get_DClr((syscfg_DClr_t *)propData, propSize, false);
|
||||
}
|
||||
propName = "device-color-policy";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
if (propSize == sizeof(uint32_t)) {
|
||||
*(uint32_t *)propData = paint_get_color_policy();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
str = env_get("network-type");
|
||||
if (str && strcmp(str, "ethernet")) noenet = 1;
|
||||
|
||||
// Find the chosen node.
|
||||
if (!FindNode(0, "chosen", &chosen)) {
|
||||
printf("UpdateDeviceTree: failed to find the /chosen node\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if WITH_NVRAM
|
||||
// <rdar://problem/9529235> race condition possible between
|
||||
// IODTNVRAM and IONVRAMController (restore loses boot-args)
|
||||
nvram_update_devicetree(chosen, "nvram-proxy-data");
|
||||
#endif
|
||||
|
||||
// Fill in the debug-enabled property
|
||||
propName = "debug-enabled";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
if (security_allow_modes(kSecurityModeKDPEnabled)) {
|
||||
((uint32_t *)propData)[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_CONSISTENT_DBG
|
||||
// Fill in the consistent-debug-root property
|
||||
propName = "consistent-debug-root";
|
||||
if(FindProperty(chosen, &propName, &propData, &propSize))
|
||||
{
|
||||
*(uint64_t*)propData = (uint64_t)(consistent_debug_get_registry());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill in the development-cert property
|
||||
propName = "development-cert";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
if (security_allow_modes(kSecurityModeDevCertAccess)) {
|
||||
((uint32_t *)propData)[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the production-cert property
|
||||
propName = "production-cert";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
if (security_allow_modes(kSecurityModeProdCertAccess)) {
|
||||
((uint32_t *)propData)[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the gid-aes-key property
|
||||
propName = "gid-aes-key";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
if (security_allow_modes(kSecurityModeGIDKeyAccess)) {
|
||||
((uint32_t *)propData)[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the uid-aes-key property
|
||||
propName = "uid-aes-key";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
if (security_allow_modes(kSecurityModeUIDKeyAccess)) {
|
||||
((uint32_t *)propData)[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the secure-boot property
|
||||
propName = "secure-boot";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
if (security_allow_modes(kSecurityStatusSecureBoot)) {
|
||||
((uint32_t *)propData)[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the certificate-production-status (raw production mode) property
|
||||
propName = "certificate-production-status";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = platform_get_raw_production_mode();
|
||||
}
|
||||
|
||||
// Fill in the certificate-security-mode (raw security mode) property
|
||||
propName = "certificate-security-mode";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = platform_get_secure_mode();
|
||||
}
|
||||
|
||||
// Fill in the effective-production-status-ap property
|
||||
propName = "effective-production-status-ap";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = platform_get_current_production_mode();
|
||||
}
|
||||
|
||||
// Fill in the effective-security-mode-ap property
|
||||
// This is for legacy reasons. As far as the AP is concerned, its effective security mode
|
||||
// doesn't change with demotion
|
||||
// <rdar://problem/16434259> Get rid of effective-security-mode-ap device tree property
|
||||
propName = "effective-security-mode-ap";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = platform_get_secure_mode();
|
||||
}
|
||||
|
||||
#if WITH_IMAGE4
|
||||
// Fill in the boot-nonce property
|
||||
propName = "boot-nonce";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
uint64_t boot_nonce = platform_get_nonce();
|
||||
memcpy(propData, &boot_nonce, propSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill in the memory vendor info
|
||||
propName = "dram-vendor";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
dram_vendor_string = platform_get_memory_manufacturer_string();
|
||||
strlcpy(propData, dram_vendor_string, propSize);
|
||||
}
|
||||
|
||||
#if WITH_SYSCFG
|
||||
// Fill in the software-behavior property
|
||||
propName = "software-behavior";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('SwBh', propData, propSize);
|
||||
}
|
||||
|
||||
// Fill in the software-bundle-version property
|
||||
propName = "software-bundle-version";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('SBVr', propData, propSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill in the system-trusted property
|
||||
propName = "system-trusted";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
if (security_allow_modes(kSecurityStatusSystemTrusted)) {
|
||||
((uint32_t *)propData)[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the board-id property
|
||||
propName = "board-id";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = platform_get_board_id();
|
||||
}
|
||||
|
||||
// Fill in the chip-id property
|
||||
propName = "chip-id";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = platform_get_chip_id();
|
||||
}
|
||||
|
||||
// Fill in the unique-chip-id property
|
||||
propName = "unique-chip-id";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint64_t *)propData)[0] = platform_get_ecid_id();
|
||||
}
|
||||
|
||||
// Fill in the die-id property
|
||||
propName = "die-id";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint64_t *)propData)[0] = platform_get_die_id();
|
||||
}
|
||||
|
||||
// Fill in the random-seed property
|
||||
propName = "random-seed";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
random_get_bytes(propData, propSize);
|
||||
}
|
||||
|
||||
// Fill in the firmware-version
|
||||
propName = "firmware-version";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
strlcpy(propData, XBS_BUILD_TAG, propSize);
|
||||
}
|
||||
|
||||
#if WITH_TICKET
|
||||
// Fill in the root-ticket-hash property
|
||||
propName = "root-ticket-hash";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
uint8_t hash[CCSHA1_OUTPUT_SIZE];
|
||||
if (propSize >= sizeof(hash) && ticket_get_hash(hash, sizeof(hash))) {
|
||||
memcpy(propData, hash, sizeof(hash));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WITH_IMAGE4
|
||||
{
|
||||
uint8_t boot_manifest_hash[HASH_OUTPUT_SIZE];
|
||||
|
||||
propName = "boot-manifest-hash";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
if (platform_get_boot_manifest_hash(boot_manifest_hash) == 0) {
|
||||
// Fill in the boot-manifest-hash property
|
||||
if (propSize >= sizeof(boot_manifest_hash)) {
|
||||
memcpy(propData, boot_manifest_hash, sizeof(boot_manifest_hash));
|
||||
} else {
|
||||
panic("%s property is too small", propName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Rename the boot-manifest-hash property to mark it invalid
|
||||
propName[0] = '~';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the mix-n-match-prevention-status property
|
||||
propName = "mix-n-match-prevention-status";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = platform_get_mix_n_match_prevention_status();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fill in the display-rotation
|
||||
propName = "display-rotation";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = ((boot_args->video.v_depth >> 8) & 0xFF) * 90;
|
||||
}
|
||||
|
||||
// Fill in the display-rotation
|
||||
propName = "display-scale";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
((uint32_t *)propData)[0] = ((boot_args->video.v_depth >> 16) & 0xFF) + 1;
|
||||
}
|
||||
|
||||
// Fill in the bootp-response packet
|
||||
propName = "bootp-response";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
*gBootPResponse = 2;
|
||||
|
||||
ipaddr = 0;
|
||||
env_get_ipaddr("ipaddr", &ipaddr);
|
||||
memcpy(gBootPResponse + 0x10, &ipaddr, 4);
|
||||
if (env_get("gateway")) {
|
||||
env_get_ipaddr("gateway", &ipaddr);
|
||||
memcpy(gBootPResponse + 236, "\143\202\123\143", 4); //dhcp magic cookie
|
||||
memcpy(gBootPResponse + 236 + 4, "\003\004", 1 + 1); //dhcp tag=3(gateway), len = 4
|
||||
memcpy(gBootPResponse + 236 + 6, &ipaddr, 4); //dhcp gateway ip
|
||||
}
|
||||
|
||||
memcpy(propData, gBootPResponse, kBootPResponceSize);
|
||||
}
|
||||
|
||||
dt_set_prop_32(chosen, "security-domain", platform_get_security_domain());
|
||||
dt_set_prop_32(chosen, "chip-epoch", platform_get_hardware_epoch());
|
||||
|
||||
// tell the kernel which partition to root off of, unless the user was explicit
|
||||
if (!contains_boot_arg(boot_args->commandLine, "rd=", true)) {
|
||||
propName = "root-matching";
|
||||
if (FindProperty(chosen, &propName, &propData, &propSize)) {
|
||||
#if RELEASE_BUILD
|
||||
boot_partition = 1;
|
||||
#else
|
||||
boot_partition = env_get_uint("boot-partition", 0) + 1;
|
||||
#endif
|
||||
boot_partition =
|
||||
// default to the same logic as what is used for loading
|
||||
// the kernelcache, even if the user loaded that over usb.
|
||||
// The IOStorageFamily uses 1-indexed partitions
|
||||
snprintf(propData, propSize,
|
||||
"<dict><key>IOProviderClass</key><string>IOMedia</string><key>IOPropertyMatch</key><dict><key>Partition ID</key><integer>%u</integer></dict></dict>",
|
||||
boot_partition);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ASP_BASE) && defined(ASP_SIZE)
|
||||
// Update ASP region info
|
||||
if (FindNode(0, "arm-io/ans/iop-ans-nub", &node)) {
|
||||
propName = "region-base";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
((uintptr_t *) propData)[0] = platform_get_memory_region_base(kMemoryRegion_StorageProcessor);
|
||||
}
|
||||
propName = "region-size";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
((uintptr_t *) propData)[0] = platform_get_memory_region_size(kMemoryRegion_StorageProcessor);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add the platform name to the root node
|
||||
if (FindNode(0, ROOT_DTPATH, &node)) {
|
||||
propName = "platform-name";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
strlcpy(propData, PLATFORM_NAME_STRING, propSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate consistent location in device tree with mac addresses for Gestalt
|
||||
if (FindNode(0, "chosen", &node)) {
|
||||
propName = "mac-address-ethernet0";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
if (env_get("ethaddr")) {
|
||||
env_get_ethaddr("ethaddr", propData);
|
||||
} else {
|
||||
int retlen = 0;
|
||||
if (!target_get_property(TARGET_PROPERTY_ETH_MACADDR, propData, 6, &retlen)) {
|
||||
propName[0] = '~';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
propName = "mac-address-wifi0";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
if (env_get("wifiaddr")) {
|
||||
env_get_ethaddr("wifiaddr", propData);
|
||||
} else {
|
||||
int retlen = 0;
|
||||
if (!target_get_property(TARGET_PROPERTY_WIFI_MACADDR, propData, 6, &retlen)) {
|
||||
propName[0] = '~';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
propName = "mac-address-bluetooth0";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
if (env_get("btaddr")) {
|
||||
env_get_ethaddr("btaddr", propData);
|
||||
} else {
|
||||
int retlen = 0;
|
||||
if (!target_get_property(TARGET_PROPERTY_BT_MACADDR, propData, 6, &retlen)) {
|
||||
propName[0] = '~';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the ethernet address in "/"
|
||||
memset(ethaddr, 0, sizeof(ethaddr));
|
||||
if (env_get("ethaddr")) {
|
||||
env_get_ethaddr("ethaddr", ethaddr);
|
||||
} else {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_ETH_MACADDR, ethaddr, 6, &retlen);
|
||||
}
|
||||
|
||||
if (FindNode(0, ROOT_DTPATH, &node)) {
|
||||
propName = "local-mac-address";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
memcpy(propData, ethaddr, 6);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the ethernet address is ETHERNET_DTPATH
|
||||
if (FindNode(0, ETHERNET_DTPATH, &node)) {
|
||||
propName = "local-mac-address";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
memcpy(propData, ethaddr, 6);
|
||||
if (noenet) {
|
||||
propName = "compatible";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
strlcpy(propData, "xxx", propSize);
|
||||
printf("Ethernet disabled\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// store enet mac-address into 'device-mac-address' in 'usb-device' node for tethering
|
||||
else if (FindNode(0, USBDEVICE_DTPATH, &node)) {
|
||||
propName = "device-mac-address";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
memcpy(propData, ethaddr, 6);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the wifi address/calibration in the wifi node (SDIO or HSIC)
|
||||
if (FindNode(0, WIFI_DTPATH, &node)) {
|
||||
DTNodePtr usb_node;
|
||||
propName = "local-mac-address";
|
||||
if (FindProperty(node, &propName, &wifiaddr, &propSize)) {
|
||||
if (env_get("wifiaddr")) {
|
||||
env_get_ethaddr("wifiaddr", wifiaddr);
|
||||
} else {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_MACADDR, wifiaddr, 6, &retlen);
|
||||
}
|
||||
// store 'wifiaddr' into 'host-mac-address' in 'usb-device' node for tethering
|
||||
if (FindNode(0, USBDEVICE_DTPATH, &usb_node)) {
|
||||
propName = "host-mac-address";
|
||||
if (FindProperty(usb_node, &propName, &propData, &propSize)) {
|
||||
memcpy(propData, wifiaddr, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
propName = "tx-calibration";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_CALIBRATION_TX, propData, 1024, &retlen);
|
||||
}
|
||||
|
||||
propName = "tx-calibration-2.4";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_CALIBRATION_TX_24, propData, propSize, &retlen);
|
||||
}
|
||||
|
||||
propName = "tx-calibration-5.0";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_CALIBRATION_TX_50, propData, propSize, &retlen);
|
||||
}
|
||||
|
||||
propName = "rx-calibration-2.4";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_CALIBRATION_RX_24, propData, propSize, &retlen);
|
||||
}
|
||||
|
||||
propName = "rx-calibration-5.0";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_CALIBRATION_RX_50, propData, propSize, &retlen);
|
||||
}
|
||||
|
||||
propName = "rx-calibration-temp";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_CALIBRATION_RX_TEMP, propData, propSize, &retlen);
|
||||
}
|
||||
|
||||
propName = "freq-group-2g-calibration";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_CALIBRATION_FREQ_GROUP_2G, propData, propSize, &retlen);
|
||||
}
|
||||
|
||||
propName = "wireless-board-snum";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_BOARD_SNUM, propData, propSize, &retlen);
|
||||
}
|
||||
|
||||
propName = "wifi-calibration-msf";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_WIFI_WCAL, propData, propSize, &retlen);
|
||||
}
|
||||
}
|
||||
|
||||
if (FindNode(0, BT_DTPATH, &node)) {
|
||||
// Set the bt address in the bluetooth node
|
||||
propName = "local-mac-address";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
if (env_get("btaddr")) {
|
||||
env_get_ethaddr("btaddr", propData);
|
||||
} else {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_BT_MACADDR, propData, 6, &retlen);
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_SYSCFG
|
||||
// Update Bluetooth TX and RX calibration data. If calibration data is
|
||||
// missing, don't publish these properties.
|
||||
propName = "bluetooth-tx-calibration";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
if (syscfgCopyDataForTag('BTTx', propData, propSize) == -1)
|
||||
propName[0] = '~';
|
||||
|
||||
propName = "bluetooth-rx-calibration";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
if (syscfgCopyDataForTag('BTRx', propData, propSize) == -1)
|
||||
propName[0] = '~';
|
||||
#endif
|
||||
}
|
||||
|
||||
//Fill Pinto address
|
||||
if (FindNode(0, PINTO_DTPATH, &node)) {
|
||||
// Set the pinto address in the corresponding node
|
||||
propName = "local-mac-address";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
if (env_get("pintoaddr")) {
|
||||
env_get_ethaddr("pintoaddr", propData);
|
||||
} else {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_PINTO_MACADDR, propData, 6, &retlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in baseband properties as necessary (don't qualify this with presence of the
|
||||
// radio driver since there may be work to do regardless)
|
||||
if (FindNode(0, BASEBAND_DTPATH, &node)) {
|
||||
#if WITH_SYSCFG
|
||||
propName = "battery-id";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
#if WITH_HW_GASGAUGE
|
||||
if (gasgauge_get_battery_id(propData, propSize) == 0) {
|
||||
batteryId = propData;
|
||||
batteryIdSize = propSize;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
syscfgCopyDataForTag('Batt', propData, propSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
propName = "region-sku";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
int retlen = 0;
|
||||
target_get_property(TARGET_PROPERTY_BB_REGION_SKU, propData, propSize, &retlen);
|
||||
}
|
||||
}
|
||||
|
||||
// Put the battery-id, boot-voltage in the charger node
|
||||
if (FindNode(0, CHARGER_DTPATH, &node)) {
|
||||
#if WITH_SYSCFG
|
||||
propName = "battery-id";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
if ((batteryId != NULL) && (propSize == batteryIdSize)) {
|
||||
memcpy(propData, batteryId, propSize);
|
||||
}
|
||||
#if WITH_HW_GASGAUGE
|
||||
else if (gasgauge_get_battery_id(propData, propSize) != 0)
|
||||
#endif
|
||||
{
|
||||
syscfgCopyDataForTag('Batt', propData, propSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WITH_HW_POWER
|
||||
propName = "boot-voltage";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
*(uint32_t *)propData = power_get_boot_battery_level();
|
||||
}
|
||||
|
||||
propName = "boot-adapter-id";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
power_get_usb_brick_id((uint32_t *)propData, propSize / sizeof(uint32_t));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if WITH_HW_MERLOT
|
||||
// Set the lcd panel info in the merlot node
|
||||
if (FindNode(0, MERLOT_DTPATH, &node)) {
|
||||
extern const uint32_t gMerlotPanelID, gMerlotInitRegisterCount;
|
||||
extern const uint8_t *gMerlotInitRegisters;
|
||||
|
||||
propName = "lcd-panel-id";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
*(uint32_t *)propData = gMerlotPanelID;
|
||||
}
|
||||
propName = "lcd-init-register-count";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
*(uint32_t *)propData = gMerlotInitRegisterCount;
|
||||
}
|
||||
propName = "lcd-init-registers";
|
||||
if ((gMerlotInitRegisters != 0) && FindProperty(node, &propName, &propData, &propSize)) {
|
||||
memcpy(propData, gMerlotInitRegisters, gMerlotInitRegisterCount * 2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WITH_SYSCFG
|
||||
// Save the multi-touch calibration
|
||||
if (FindNode(0, MULTITOUCH_DTPATH, &node)) {
|
||||
propName = "multi-touch-calibration";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
syscfgCopyDataForTag('MtCl', propData, propSize);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t sysCfgData = 0;
|
||||
// Obtain the syscfg entry that identifies when a rear camera is absent
|
||||
result = syscfgCopyDataForTag('NoBC', (uint8_t *)(&sysCfgData), sizeof(sysCfgData));
|
||||
|
||||
// Set rear camera information
|
||||
if (FindNode(0, "arm-io/isp", &node)) {
|
||||
propName = "camera-rear";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
// if bit 0 is 1, then delete rear camera entry
|
||||
if (result == sizeof(sysCfgData) && (sysCfgData & 0x1))
|
||||
propName[0] = '~';
|
||||
}
|
||||
}
|
||||
|
||||
// Also delete rear camera related properties in product/camera if rear camera is absent
|
||||
if (FindNode(0, "product/camera", &node)) {
|
||||
|
||||
if (result == sizeof(sysCfgData) && (sysCfgData & 0x1)) {
|
||||
propName = "flash";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
propName[0] = '~';
|
||||
|
||||
propName = "rear-burst";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
propName[0] = '~';
|
||||
|
||||
propName = "rear-slowmo";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
propName[0] = '~';
|
||||
|
||||
propName = "rear-max-video-zoom";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
propName[0] = '~';
|
||||
|
||||
propName = "rear-hdr";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
propName[0] = '~';
|
||||
|
||||
propName = "panorama";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
propName[0] = '~';
|
||||
|
||||
propName = "rear-auto-hdr";
|
||||
if (FindProperty(node, &propName, &propData, &propSize))
|
||||
propName[0] = '~';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Let the platform and target make updates
|
||||
result = platform_update_device_tree();
|
||||
if (result < 0) return result;
|
||||
|
||||
// Find the iBoot node under the chosen direectory
|
||||
// as that's where the boot specific stuff goes.
|
||||
if (FindNode(0, "chosen/iBoot", &node)) {
|
||||
// Fill in the boot performance information
|
||||
extern const uint32_t gPowerOnTime;
|
||||
extern const uint32_t gDebugPromptTime;
|
||||
extern const uint32_t gLoadKernelTime;
|
||||
const uint32_t populateRegistryTime = system_time();
|
||||
|
||||
propName = "start-time";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
*(uint32_t *)propData = gPowerOnTime;
|
||||
}
|
||||
propName = "debug-wait-start";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
*(uint32_t *)propData = gDebugPromptTime;
|
||||
}
|
||||
propName = "load-kernel-start";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
*(uint32_t *)propData = gLoadKernelTime;
|
||||
}
|
||||
propName = "populate-registry-time";
|
||||
if (FindProperty(node, &propName, &propData, &propSize)) {
|
||||
*(uint32_t *)propData = populateRegistryTime;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,793 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2013 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 1998-2006 Apple Computer, 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 <drivers/display.h>
|
||||
#include <drivers/power.h>
|
||||
#include <lib/profile.h>
|
||||
#include <lib/cksum.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include <lib/env.h>
|
||||
#include <lib/image.h>
|
||||
#include <lib/lzss.h>
|
||||
#include <lib/macho.h>
|
||||
#include <lib/random.h>
|
||||
#include <lib/syscfg.h>
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <sys.h>
|
||||
#include <target.h>
|
||||
|
||||
#include "boot.h"
|
||||
#include "macho_header.h"
|
||||
|
||||
#if __arm64__
|
||||
#define L2_GRANULE (PAGE_SIZE * (PAGE_SIZE / 8))
|
||||
#define L2_GRANULE_MASK (L2_GRANULE - 1)
|
||||
#endif
|
||||
|
||||
#if !WITH_MONITOR
|
||||
addr_t gKernelEntry; // Kernel entry point
|
||||
#endif
|
||||
|
||||
/* From dt.c */
|
||||
extern int UpdateDeviceTree(const boot_args *boot_args);
|
||||
|
||||
/* From lib/ramdisk/ramdisk.c */
|
||||
extern addr_t gRAMDiskAddr;
|
||||
extern size_t gRAMDiskSize;
|
||||
|
||||
struct compressed_kernel_header {
|
||||
uint32_t signature;
|
||||
uint32_t compress_type;
|
||||
uint32_t adler32;
|
||||
uint32_t uncompressed_size;
|
||||
uint32_t compressed_size;
|
||||
uint32_t reserved[11];
|
||||
uint8_t platform_name[64];
|
||||
uint8_t root_path[256];
|
||||
uint8_t data[];
|
||||
};
|
||||
typedef struct compressed_kernel_header compressed_kernel_header;
|
||||
|
||||
static int loaded_kernelcache(addr_t secure_addr, size_t secure_size, addr_t *entry, addr_t *boot_args_out);
|
||||
static int valid_kernelcache(addr_t addr, size_t size);
|
||||
static size_t decompress_kernelcache(addr_t addr, size_t maxsize);
|
||||
static addr_t load_kernelcache_object(addr_t addr, size_t size, addr_t *boot_args_out);
|
||||
static int valid_monitor(addr_t addr, size_t size, addr_t *monitor_addr, size_t *monitor_size);
|
||||
static void record_kernelcache_segments(addr_t addr, addr_t phys_base);
|
||||
static void record_kernelcache_segment(addr_t segCmd, addr_t phys_base, addr_t *virt_base);
|
||||
static bool record_memory_range(char *rangeName, addr_t start, size_t length);
|
||||
static addr_t alloc_kernel_mem(size_t size);
|
||||
static void init_kernel_mem_allocator(void);
|
||||
static addr_t kern_to_phys(addr_t vmaddr, boot_args *boot_args);
|
||||
static int load_and_set_device_tree(void);
|
||||
|
||||
int
|
||||
load_kernelcache(addr_t addr, size_t len, uint32_t type, addr_t *entry, addr_t *boot_args_out)
|
||||
{
|
||||
addr_t secure_addr;
|
||||
size_t secure_size;
|
||||
|
||||
if (len > DEFAULT_KERNEL_SIZE) {
|
||||
printf("Kernelcache too large\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
secure_addr = DEFAULT_KERNEL_ADDRESS;
|
||||
secure_size = DEFAULT_KERNEL_SIZE;
|
||||
|
||||
if (image_load_memory(addr, len, &secure_addr, &secure_size, &type, 1, NULL, 0)) {
|
||||
printf("Kernelcache image not valid\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
return(loaded_kernelcache(secure_addr, secure_size, entry, boot_args_out));
|
||||
}
|
||||
|
||||
#if WITH_FS
|
||||
int
|
||||
load_kernelcache_file(const char *path, uint32_t type, addr_t *entry, addr_t *boot_args_out)
|
||||
{
|
||||
addr_t secure_addr;
|
||||
size_t secure_size;
|
||||
|
||||
/*
|
||||
* We could stat the file here to perform a size check, but the load-from-disk
|
||||
* case is less likely to have an accidentally-oversize kernelcache.
|
||||
*/
|
||||
|
||||
secure_addr = DEFAULT_KERNEL_ADDRESS;
|
||||
secure_size = DEFAULT_KERNEL_SIZE;
|
||||
|
||||
PROFILE_ENTER('ILF');
|
||||
if (image_load_file(path, &secure_addr, &secure_size, &type, 1, NULL, 0)) {
|
||||
printf("Kernelcache image not valid\n");
|
||||
return -3;
|
||||
}
|
||||
PROFILE_EXIT('ILF');
|
||||
|
||||
return(loaded_kernelcache(secure_addr, secure_size, entry, boot_args_out));
|
||||
}
|
||||
#endif /* WITH_FS */
|
||||
|
||||
int load_kernelcache_image(struct image_info *image, uint32_t type, addr_t *entry, addr_t *boot_args_out)
|
||||
{
|
||||
addr_t secure_addr;
|
||||
size_t secure_size;
|
||||
|
||||
secure_addr = DEFAULT_KERNEL_ADDRESS;
|
||||
secure_size = DEFAULT_KERNEL_SIZE;
|
||||
|
||||
if (image_load(image, &type, 1, NULL, (void **)&secure_addr, &secure_size)) {
|
||||
printf("Kernelcache image not valid\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
return(loaded_kernelcache(secure_addr, secure_size, entry, boot_args_out));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
loaded_kernelcache(addr_t secure_addr, size_t secure_size, addr_t *entry, addr_t *boot_args_out)
|
||||
{
|
||||
addr_t addr;
|
||||
#if WITH_MONITOR
|
||||
addr_t monitor_addr;
|
||||
size_t monitor_size;
|
||||
#endif
|
||||
|
||||
/* consolidate environment */
|
||||
security_consolidate_environment();
|
||||
|
||||
/* Load and setup devicetree. This needs to happen here to retrieve information regarding
|
||||
* monitor memory
|
||||
*/
|
||||
if (load_and_set_device_tree() != 0) {
|
||||
printf("Failed to setup devicetree \n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
#if WITH_MONITOR
|
||||
/* Find total kernelcache file size from compressed header, and look for monitor at the end.
|
||||
* If theres a monitor, load it now, then load kernelcache
|
||||
*/
|
||||
if (valid_monitor(secure_addr, secure_size, &monitor_addr, &monitor_size) != 0) {
|
||||
load_monitor_image(monitor_addr, monitor_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!valid_kernelcache(secure_addr, secure_size))
|
||||
return -4;
|
||||
|
||||
PROFILE_ENTER('DKc');
|
||||
secure_size = decompress_kernelcache(secure_addr, DEFAULT_KERNEL_SIZE);
|
||||
if (secure_size == 0)
|
||||
return -5;
|
||||
PROFILE_EXIT('DKc');
|
||||
|
||||
if (!macho_valid(secure_addr))
|
||||
return -6;
|
||||
|
||||
security_protect_memory(0, 0, true);
|
||||
|
||||
PROFILE_ENTER('LKO');
|
||||
addr = load_kernelcache_object(secure_addr, secure_size, boot_args_out);
|
||||
PROFILE_EXIT('LKO');
|
||||
if (addr == 0) {
|
||||
/* Re-init security and clear devicetree, ramdisk */
|
||||
security_init(true);
|
||||
|
||||
return -7;
|
||||
}
|
||||
|
||||
*entry = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
valid_kernelcache(addr_t addr, size_t size)
|
||||
{
|
||||
compressed_kernel_header *kernel_header;
|
||||
|
||||
kernel_header = (compressed_kernel_header *)addr;
|
||||
if (ntohl(kernel_header->signature) != 'comp')
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t
|
||||
decompress_kernelcache(addr_t addr, size_t maxsize)
|
||||
{
|
||||
compressed_kernel_header *kernel_header;
|
||||
size_t tmp_size, comp_size, uncomp_size;
|
||||
uint8_t *uncomp_buffer, *comp_buffer = NULL;
|
||||
uint32_t kernel_adler32;
|
||||
size_t result = 0;
|
||||
|
||||
kernel_header = (compressed_kernel_header *)(addr);
|
||||
if (ntohl(kernel_header->signature) != 'comp') {
|
||||
dprintf(DEBUG_CRITICAL, "unknown kernelcache signature\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ntohl(kernel_header->compress_type) != 'lzss') {
|
||||
dprintf(DEBUG_CRITICAL, "unknown kernelcache compression type\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dprintf(DEBUG_CRITICAL, "Loading kernel cache at %p\n", (void *)addr);
|
||||
|
||||
// For now, ignore platform name and root path
|
||||
|
||||
// Save the adler32 checksum for the kernel cache
|
||||
kernel_adler32 = ntohl(kernel_header->adler32);
|
||||
|
||||
// Ensure that the uncompressed kernel cache is not too large
|
||||
uncomp_size = ntohl(kernel_header->uncompressed_size);
|
||||
if (uncomp_size > maxsize) {
|
||||
dprintf(DEBUG_CRITICAL, "Uncompressed size too large: 0x%08lx, max 0x%08lx\n", uncomp_size, maxsize);
|
||||
goto exit;
|
||||
}
|
||||
uncomp_buffer = (uint8_t *)addr;
|
||||
|
||||
// Allocate buffer for the compressed kernel cache
|
||||
comp_size = ntohl(kernel_header->compressed_size);
|
||||
comp_buffer = malloc(comp_size);
|
||||
|
||||
// Copy the compressed kernel cache to the new buffer
|
||||
memcpy(comp_buffer, &kernel_header->data[0], comp_size);
|
||||
|
||||
tmp_size = decompress_lzss(uncomp_buffer, maxsize, comp_buffer, comp_size);
|
||||
if (tmp_size != uncomp_size) {
|
||||
dprintf(DEBUG_CRITICAL, "Size mismatch from lzss 0x%08lx, should be 0x%08lx\n", tmp_size, uncomp_size);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (kernel_adler32 != adler32(uncomp_buffer, uncomp_size)) {
|
||||
dprintf(DEBUG_CRITICAL, "Adler32 mismatch\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dprintf(DEBUG_CRITICAL, "Uncompressed kernel cache at %p\n", uncomp_buffer);
|
||||
|
||||
result = uncomp_size;
|
||||
|
||||
exit:
|
||||
// Zero and free the buffer for the compressed kernel cache
|
||||
if (comp_buffer != 0) {
|
||||
memset(comp_buffer, 0, comp_size);
|
||||
free(comp_buffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
valid_monitor(addr_t addr, size_t size, addr_t *monitor_addr, size_t *monitor_size)
|
||||
{
|
||||
compressed_kernel_header *kernel_header;
|
||||
size_t kernel_size;
|
||||
|
||||
*monitor_addr = 0;
|
||||
*monitor_size = 0;
|
||||
|
||||
kernel_header = (compressed_kernel_header *)addr;
|
||||
if (ntohl(kernel_header->signature) != 'comp')
|
||||
return 0;
|
||||
|
||||
kernel_size = ntohl(kernel_header->compressed_size) + sizeof(compressed_kernel_header);
|
||||
|
||||
if (size > kernel_size) {
|
||||
if (!macho_valid(addr + kernel_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*monitor_addr = addr + kernel_size;
|
||||
*monitor_size = (size - kernel_size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
load_and_set_device_tree(void)
|
||||
{
|
||||
/* Load device-tree here. We need to know size of certain memory regions at this point.
|
||||
* Example: tz1 region to load monitor
|
||||
*/
|
||||
if (dt_load() != 0) {
|
||||
dprintf(DEBUG_CRITICAL, "failed to load device tree\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tokenize the string based on whitespace and look for
|
||||
* each token to have the specified value/prefix
|
||||
*
|
||||
* XXX In general, using this function is an admission
|
||||
* that you haven't architected things correctly.
|
||||
* boot-args should normally NOT BE USED for
|
||||
* communication with anything other than the very
|
||||
* early stages of the kernel startup process.
|
||||
* For all other applications, use the DeviceTree.
|
||||
*
|
||||
* XXX This is also not terribly well implemented. The
|
||||
* search can be phrased as a substring search for
|
||||
* prefixmatch ? " <arg>" : " <arg> "
|
||||
* removing much of the manual token scanning.
|
||||
*/
|
||||
bool
|
||||
contains_boot_arg(const char *bootArgs,
|
||||
const char *arg,
|
||||
bool prefixmatch)
|
||||
{
|
||||
const char *pos = bootArgs;
|
||||
size_t tokenlen;
|
||||
size_t arglen = strlen(arg);
|
||||
|
||||
while (*pos) {
|
||||
// skip leading whitespace
|
||||
while (*pos && *pos == ' ') {
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (*pos == '\0') {
|
||||
// got to the end of the string, this arg isn't present
|
||||
return false;
|
||||
}
|
||||
|
||||
tokenlen = 0;
|
||||
while (pos[tokenlen] != '\0' && pos[tokenlen] != ' ') {
|
||||
tokenlen++;
|
||||
}
|
||||
|
||||
// the current token may end at a space or the end of the buffer,
|
||||
// so don't dereference past it
|
||||
|
||||
if (tokenlen >= arglen) {
|
||||
if (prefixmatch && strncmp(pos, arg, arglen) == 0) {
|
||||
return true;
|
||||
} else if (strncmp(pos, arg, tokenlen) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pos += tokenlen;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void update_display_info(boot_args *boot_args)
|
||||
{
|
||||
struct display_info info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
display_get_info(&info);
|
||||
|
||||
boot_args->video.v_rowBytes = info.stride;
|
||||
boot_args->video.v_width = info.width;
|
||||
boot_args->video.v_height = info.height;
|
||||
boot_args->video.v_depth = info.depth;
|
||||
boot_args->video.v_baseAddr = (addr_t)info.framebuffer;
|
||||
boot_args->video.v_display = 1;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_kaslr_random(const char *env_var)
|
||||
{
|
||||
size_t value;
|
||||
|
||||
#if DEBUG_BUILD
|
||||
// Debug builds can also specify a specific slide for debug reasons
|
||||
value = env_get_uint(env_var, (size_t)-1);
|
||||
if (value != (size_t)-1) {
|
||||
printf("got static value 0x%08lx for %s\n", value, env_var);
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
random_get_bytes((void *)&value, sizeof(value));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
get_kaslr_slides(uintptr_t kernel_phys_base, size_t kernel_size, size_t *physical_slide_out, size_t *virtual_slide_out)
|
||||
{
|
||||
bool kaslr_off = false;
|
||||
size_t slide_phys = 0;
|
||||
size_t slide_virt = 0;
|
||||
|
||||
#if !RELEASE_BUILD
|
||||
// Non-release builds can disable ASLR for debug reasons
|
||||
kaslr_off = env_get_bool("kaslr-off", false);
|
||||
#endif
|
||||
|
||||
#if PAGE_SIZE != 16384
|
||||
if (!kaslr_off) {
|
||||
size_t random_value = get_kaslr_random("kaslr-slide") & 0xff;
|
||||
if (random_value == 0)
|
||||
random_value = 0x100;
|
||||
slide_virt = 0x1000000 + (random_value << 21);
|
||||
} else {
|
||||
slide_virt = 0;
|
||||
}
|
||||
slide_phys = 0;
|
||||
#else
|
||||
#if DEFAULT_KERNEL_SIZE + L2_GRANULE > DEFAULT_LOAD_SIZE
|
||||
#error "kernelcache loader needs extra space to do physical kernel slide"
|
||||
#endif
|
||||
const size_t slide_granule = (1 << 21);
|
||||
const size_t slide_granule_mask = slide_granule - 1;
|
||||
const size_t slide_virt_max = 0x100 * (2*1024*1024);
|
||||
|
||||
ASSERT((kernel_phys_base & L2_GRANULE_MASK) == 0);
|
||||
if (!kaslr_off) {
|
||||
// Slide the kernel virtually, keeping in the min and max range
|
||||
// defined above
|
||||
size_t random_value = get_kaslr_random("kaslr-slide");
|
||||
slide_virt = (random_value & ~slide_granule_mask) % slide_virt_max;
|
||||
if (slide_virt == 0) {
|
||||
slide_virt = slide_virt_max;
|
||||
}
|
||||
|
||||
// The virtual and physical addresses need to have the same offset
|
||||
// within an L2 block (2MiB with 4k pages, 32MiB with 16k).
|
||||
// By contract the kernel's virtual base address is aligned to an L2 block.
|
||||
// We asserted above that the physical base address is aligned to an L2 block.
|
||||
// Under those conditions, guaranteeing the alignment is trivial.
|
||||
slide_phys = (slide_virt & L2_GRANULE_MASK);
|
||||
} else {
|
||||
slide_virt = 0;
|
||||
slide_phys = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
dprintf(DEBUG_INFO, "KASLR virtual slide: 0x%08zx\n", slide_virt);
|
||||
dprintf(DEBUG_INFO, "KASLR physical slide: 0x%08zx\n", slide_phys);
|
||||
|
||||
*physical_slide_out = slide_phys;
|
||||
*virtual_slide_out = slide_virt;
|
||||
}
|
||||
|
||||
|
||||
static addr_t
|
||||
load_kernelcache_object(addr_t addr, size_t size, addr_t *boot_args_out)
|
||||
{
|
||||
static boot_args boot_args;
|
||||
const char *bootArgsStr;
|
||||
addr_t kernel_region_phys;
|
||||
size_t kernel_region_size;
|
||||
addr_t kcache_base_kern;
|
||||
addr_t entry_point_kern;
|
||||
addr_t entry_point_phys;
|
||||
addr_t kcache_end_kern;
|
||||
addr_t ramdisk_kern;
|
||||
addr_t ramdisk_phys;
|
||||
addr_t boot_args_kern;
|
||||
addr_t boot_args_phys;
|
||||
addr_t devicetree_kern;
|
||||
addr_t devicetree_phys;
|
||||
size_t devicetree_size;
|
||||
addr_t last_alloc_addr_kern;
|
||||
addr_t last_alloc_addr_phys;
|
||||
size_t slide_phys;
|
||||
size_t slide_kern;
|
||||
|
||||
// Reset the kernel memory allocator in case this is our second time through
|
||||
init_kernel_mem_allocator();
|
||||
// Ditto with boot args
|
||||
memset(&boot_args, 0, sizeof(boot_args));
|
||||
|
||||
boot_args.revision = kBootArgsRevision;
|
||||
boot_args.version = kBootArgsVersion1 + platform_get_security_epoch();
|
||||
|
||||
// Get the region of memory that will be managed by the kernel.
|
||||
// This is typically above the SEP region (if any) or
|
||||
// at the base of memory if no SEP
|
||||
kernel_region_phys = platform_get_memory_region_base(kMemoryRegion_Kernel);
|
||||
kernel_region_size = platform_get_memory_region_size(kMemoryRegion_Kernel);
|
||||
|
||||
get_kaslr_slides(kernel_region_phys, size, &slide_phys, &slide_kern);
|
||||
RELEASE_ASSERT(slide_phys + size < kernel_region_size);
|
||||
|
||||
boot_args.physBase = kernel_region_phys;
|
||||
boot_args.memSize = kernel_region_size;
|
||||
|
||||
// Scan the kernelcache and record the location of segments in the
|
||||
// /chosen/memory-map node in the devicetree.
|
||||
record_kernelcache_segments(addr, kernel_region_phys + slide_phys);
|
||||
|
||||
// Load the kernelcache object
|
||||
macho_load(addr, size, kernel_region_phys + slide_phys, &kcache_base_kern, &kcache_end_kern, &entry_point_kern, slide_kern);
|
||||
boot_args.virtBase = kcache_base_kern - slide_phys;
|
||||
// Record the space used by the kernelcache
|
||||
alloc_kernel_mem(kcache_end_kern);
|
||||
|
||||
#if RELEASE_BUILD
|
||||
// Force boot-args for RELEASE builds
|
||||
bootArgsStr = (gRAMDiskAddr == 0) ? "" : "rd=md0 nand-enable-reformat=1 -progress";
|
||||
#else
|
||||
bootArgsStr = env_get("boot-args");
|
||||
if (bootArgsStr == 0)
|
||||
bootArgsStr = "";
|
||||
#endif
|
||||
|
||||
if (target_is_tethered()) {
|
||||
snprintf(boot_args.commandLine, BOOT_LINE_LENGTH, "%s force-usb-power=1 ", bootArgsStr);
|
||||
} else {
|
||||
snprintf(boot_args.commandLine, BOOT_LINE_LENGTH, "%s ", bootArgsStr);
|
||||
}
|
||||
|
||||
if (gRAMDiskAddr != 0) {
|
||||
uint32_t backlight_level;
|
||||
if (target_get_property(TARGET_PROPERTY_RESTORE_BACKLIGHT_LEVEL, &backlight_level, sizeof(backlight_level), NULL)) {
|
||||
snprintf(boot_args.commandLine, BOOT_LINE_LENGTH, "%s backlight-level=%d ", bootArgsStr, backlight_level);
|
||||
}
|
||||
}
|
||||
|
||||
bootArgsStr = boot_args.commandLine;
|
||||
|
||||
#if WITH_HW_POWER
|
||||
boot_args.boot_flags |= (power_is_dark_boot()) ? kBootFlagDarkBoot : 0;
|
||||
|
||||
dprintf(DEBUG_INFO, "Passing dark boot state: %s\n", power_is_dark_boot() ? "dark" : "not dark");
|
||||
#endif
|
||||
|
||||
update_display_info(&boot_args);
|
||||
|
||||
if (contains_boot_arg(bootArgsStr, "-s", false)
|
||||
|| contains_boot_arg(bootArgsStr, "-v", false)) {
|
||||
boot_args.video.v_display = 0;
|
||||
}
|
||||
|
||||
if (contains_boot_arg(bootArgsStr, "debug=", true)) {
|
||||
security_enable_kdp();
|
||||
}
|
||||
|
||||
#if WITH_HW_USB
|
||||
if (contains_boot_arg(bootArgsStr, "force-usb-power=1", false)) {
|
||||
extern bool force_usb_power;
|
||||
force_usb_power = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we have a ramdisk, allocate kernel memory for it and copy
|
||||
// it from its load address to the kernel region
|
||||
if (gRAMDiskAddr != 0) {
|
||||
ramdisk_kern = alloc_kernel_mem(gRAMDiskSize);
|
||||
ramdisk_phys = kern_to_phys(ramdisk_kern, &boot_args);
|
||||
memcpy((void *)ramdisk_phys, (void *)gRAMDiskAddr, gRAMDiskSize);
|
||||
record_memory_range("RAMDisk", ramdisk_phys, gRAMDiskSize);
|
||||
}
|
||||
|
||||
// Allocate a kernel memory for the boot args. The boot args
|
||||
// will get copied to this buffer at the end of this function.
|
||||
boot_args_kern = alloc_kernel_mem(PAGE_SIZE);
|
||||
boot_args_phys = kern_to_phys(boot_args_kern, &boot_args);
|
||||
record_memory_range("BootArgs", boot_args_phys, PAGE_SIZE);
|
||||
|
||||
PROFILE_ENTER('UDT');
|
||||
int ret = UpdateDeviceTree(&boot_args);
|
||||
PROFILE_EXIT('UDT');
|
||||
if (ret < 0){
|
||||
dprintf(DEBUG_CRITICAL, "UpdateDeviceTree() failed with %d\n", ret);
|
||||
return (addr_t)(NULL);
|
||||
}
|
||||
|
||||
// We need to put the size of the device tree into the device tree, but
|
||||
// doing so could change its size. So, start by recording dummy information
|
||||
// and then calculate the final device tree size
|
||||
record_memory_range("DeviceTree", (addr_t)-1, (size_t)-1);
|
||||
|
||||
devicetree_size = dt_get_size();
|
||||
|
||||
// Allocate kernel memory for the device tree
|
||||
devicetree_kern = alloc_kernel_mem(devicetree_size);
|
||||
devicetree_phys = kern_to_phys(devicetree_kern, &boot_args);
|
||||
// overwrite the dummy entry made previously
|
||||
record_memory_range("DeviceTree", devicetree_phys, devicetree_size);
|
||||
|
||||
dt_serialize(NULL, (void *)devicetree_phys, devicetree_size);
|
||||
|
||||
// After this point, the device tree must not be modified
|
||||
dt_seal();
|
||||
|
||||
boot_args.deviceTreeP = (void *)devicetree_kern;
|
||||
boot_args.deviceTreeLength = devicetree_size;
|
||||
|
||||
entry_point_phys = kern_to_phys(entry_point_kern, &boot_args);
|
||||
|
||||
last_alloc_addr_kern = (alloc_kernel_mem(0) + 0x3000) & (size_t)~0x3fff;
|
||||
last_alloc_addr_phys = kern_to_phys(last_alloc_addr_kern, &boot_args);
|
||||
|
||||
boot_args.topOfKernelData = last_alloc_addr_phys;
|
||||
|
||||
dprintf(DEBUG_CRITICAL, "boot_args.commandLine = [%s]\n", boot_args.commandLine);
|
||||
dprintf(DEBUG_INFO, "boot_args.physBase = [%p], boot_args.virtBase = [%p]\n", (void *)boot_args.physBase, (void *)boot_args.virtBase);
|
||||
dprintf(DEBUG_INFO, "boot_args.deviceTreeLength = 0x%x, boot_args.deviceTreeP = %p, devicetree_phys = %p\n",
|
||||
boot_args.deviceTreeLength, (void *)boot_args.deviceTreeP, (void *)devicetree_phys);
|
||||
dprintf(DEBUG_INFO, "kernel entry point: %p, boot args: %p\n", (void *)entry_point_phys, (void *)boot_args_phys);
|
||||
|
||||
// copy our local boot arg buffer into the kernel memory region for boot args
|
||||
memset((void *)boot_args_phys, 0, PAGE_SIZE);
|
||||
memcpy((void *)boot_args_phys, &boot_args, sizeof(boot_args));
|
||||
|
||||
security_create_sleep_token(SLEEP_TOKEN_BUFFER_BASE + kSleepTokeniBootOffset);
|
||||
|
||||
#if !RELEASE_BUILD && WITH_CONSISTENT_DEBUG
|
||||
// Astris needs to be able to find the kernel to do coredumps
|
||||
dbg_record_header_t kernel_region_hdr;
|
||||
kernel_region_hdr.record_id = kDbgIdXNUKernelRegion;
|
||||
kernel_region_hdr.physaddr = kernel_region_phys + slide_phys;
|
||||
kernel_region_hdr.length = kcache_end_kern - kcache_base_kern;
|
||||
consistent_debug_register_header(kernel_region_hdr);
|
||||
#endif
|
||||
|
||||
#if WITH_MONITOR
|
||||
extern addr_t gMonitorArgs;
|
||||
extern addr_t gMonitorEntry;
|
||||
monitor_boot_args *mba;
|
||||
|
||||
/*
|
||||
* Patch the monitor boot-args with the location of the
|
||||
* now-constructed kernel boot-args.
|
||||
* Ensure that we pass back the monitor entrypoint, not
|
||||
* the kernel's.
|
||||
*/
|
||||
if (0 != gMonitorArgs) {
|
||||
mba = (struct monitor_boot_args *)gMonitorArgs;
|
||||
mba->kernArgs = boot_args_phys;
|
||||
mba->kernEntry = entry_point_phys;
|
||||
mba->kernPhysBase = kernel_region_phys;
|
||||
mba->kernPhysSlide = slide_phys;
|
||||
mba->kernVirtSlide = slide_kern;
|
||||
|
||||
dprintf(DEBUG_INFO, "monitor entry point: %p, boot args: %p\n", (void *)gMonitorEntry, (void *)gMonitorArgs);
|
||||
|
||||
*boot_args_out = gMonitorArgs;
|
||||
return gMonitorEntry;
|
||||
}
|
||||
#else
|
||||
gKernelEntry = entry_point_phys;
|
||||
#endif
|
||||
|
||||
*boot_args_out = boot_args_phys;
|
||||
return entry_point_phys;
|
||||
}
|
||||
|
||||
// Private Functions
|
||||
|
||||
/*
|
||||
* Scan the mach-o kernelcache and record the location
|
||||
* of interesting segments in the chosen/memory-map node
|
||||
* of the DeviceTree.
|
||||
*/
|
||||
static void
|
||||
record_kernelcache_segments(addr_t addr, addr_t phys_base)
|
||||
{
|
||||
mach_header_t *mH;
|
||||
addr_t cmdBase;
|
||||
addr_t virt_base;
|
||||
uint32_t index;
|
||||
|
||||
mH = (mach_header_t *)addr;
|
||||
cmdBase = (addr_t)(mH + 1);
|
||||
virt_base = 0;
|
||||
|
||||
for (index = 0; index < mH->ncmds; index++) {
|
||||
#if defined(__LP64__)
|
||||
if ((((struct load_command *)cmdBase)->cmd) == LC_SEGMENT_64)
|
||||
#else
|
||||
if ((((struct load_command *)cmdBase)->cmd) == LC_SEGMENT)
|
||||
#endif
|
||||
record_kernelcache_segment(cmdBase, phys_base, &virt_base);
|
||||
cmdBase += (((struct load_command *)cmdBase)->cmdsize);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
record_kernelcache_segment(addr_t cmdBase, addr_t phys_base, addr_t *virt_base)
|
||||
{
|
||||
segment_command_t *segCmd;
|
||||
char rangeName[kPropNameLength];
|
||||
addr_t pmaddr;
|
||||
addr_t vmaddr;
|
||||
size_t vmsize;
|
||||
|
||||
segCmd = (segment_command_t *)cmdBase;
|
||||
|
||||
vmaddr = segCmd->vmaddr;
|
||||
vmsize = segCmd->vmsize;
|
||||
|
||||
// if virt_base has not been set let vmaddr select a segment (1MB)
|
||||
if (*virt_base == 0) {
|
||||
*virt_base = vmaddr & (addr_t)~0xfffff;
|
||||
}
|
||||
|
||||
pmaddr = phys_base + vmaddr - *virt_base;
|
||||
|
||||
if (strcmp(segCmd->segname, "__PAGEZERO")) {
|
||||
// Add the Segment to the memory-map.
|
||||
snprintf(rangeName, kPropNameLength, "Kernel-%s", segCmd->segname);
|
||||
record_memory_range(rangeName, pmaddr, vmsize);
|
||||
}
|
||||
}
|
||||
|
||||
/* used in the chosen/memory-map node */
|
||||
typedef struct MemoryMapFileInfo {
|
||||
addr_t paddr;
|
||||
size_t length;
|
||||
} MemoryMapFileInfo;
|
||||
|
||||
static bool
|
||||
record_memory_range(char *rangeName, addr_t start, size_t length)
|
||||
{
|
||||
dt_node_t *memory_map;
|
||||
MemoryMapFileInfo prop_data;
|
||||
|
||||
/*
|
||||
* Find the /chosen/memory-map node.
|
||||
*/
|
||||
if (!dt_find_node(0, "chosen/memory-map", &memory_map)) {
|
||||
dprintf(DEBUG_INFO, "no /chosen/memory-map in DeviceTree\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
prop_data.paddr = start;
|
||||
prop_data.length = length;
|
||||
|
||||
dt_set_prop(memory_map, rangeName, &prop_data, sizeof(prop_data));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static addr_t last_kernel_addr;
|
||||
|
||||
static void
|
||||
init_kernel_mem_allocator(void)
|
||||
{
|
||||
last_kernel_addr = 0;
|
||||
}
|
||||
|
||||
static addr_t
|
||||
alloc_kernel_mem(size_t size)
|
||||
{
|
||||
addr_t addr;
|
||||
|
||||
addr = last_kernel_addr;
|
||||
|
||||
last_kernel_addr += (size + PAGE_SIZE - 1) & ~(addr_t)(PAGE_SIZE - 1);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a kernel-virtual address to a physical address.
|
||||
*/
|
||||
static addr_t
|
||||
kern_to_phys(addr_t vmaddr, boot_args *boot_args)
|
||||
{
|
||||
if (boot_args->virtBase == 0)
|
||||
panic("kern_to_phys without setting kernel virtual base");
|
||||
|
||||
return(vmaddr - boot_args->virtBase + boot_args->physBase);
|
||||
}
|
||||
@@ -0,0 +1,544 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2011 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 1998-2006 Apple Computer, 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.
|
||||
*/
|
||||
/*
|
||||
* macho.c - Functions for decoding a Mach-o Kernel.
|
||||
*
|
||||
* Copyright (c) 1998-2006 Apple Computer, Inc.
|
||||
*
|
||||
* DRI: Josh de Cesare
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <lib/macho.h>
|
||||
#include <platform.h>
|
||||
|
||||
#include "macho_header.h"
|
||||
|
||||
static addr_t gRelocBase = (addr_t)NULL;
|
||||
static addr_t gLinkeditBase = (addr_t)NULL;
|
||||
static uint32_t gLinkeditOffset = 0;
|
||||
static uint32_t gkalsr_debug = DEBUG_SPEW;
|
||||
|
||||
static bool ProcessSymbols(addr_t cmdBase, size_t slide);
|
||||
|
||||
static bool ProcessRelocations(addr_t cmdBase, size_t slide);
|
||||
|
||||
static segment_command_t *firstsegfromheader(mach_header_t *header);
|
||||
|
||||
static segment_command_t *nextsegfromheader(mach_header_t *header, segment_command_t *seg);
|
||||
|
||||
static section_t *firstsect(segment_command_t *sgp);
|
||||
|
||||
static section_t *nextsect(segment_command_t *sgp, section_t *sp);
|
||||
|
||||
static bool macho_load_segment(addr_t imageAddr,
|
||||
size_t imageSize,
|
||||
addr_t segmentAddr,
|
||||
addr_t loadAddr,
|
||||
addr_t *virtualBase,
|
||||
addr_t *virtualEnd,
|
||||
size_t slide);
|
||||
|
||||
/*
|
||||
* Check whether something might be a Mach-O object.
|
||||
*/
|
||||
bool
|
||||
macho_valid(addr_t addr)
|
||||
{
|
||||
return(((mach_header_t *)addr)->magic == MH_MAGIC);
|
||||
}
|
||||
|
||||
bool
|
||||
macho_slideable(addr_t addr)
|
||||
{
|
||||
mach_header_t *mH;
|
||||
|
||||
mH = (mach_header_t *)addr;
|
||||
|
||||
if (!macho_valid(addr))
|
||||
return false;
|
||||
|
||||
return (mH->flags & MH_PIE) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a mach-o object.
|
||||
*/
|
||||
bool
|
||||
macho_load(
|
||||
addr_t imageAddr,
|
||||
size_t imageSize,
|
||||
addr_t loadAddr,
|
||||
addr_t *virtualBase,
|
||||
addr_t *virtualEnd,
|
||||
addr_t *entryPoint,
|
||||
size_t slide)
|
||||
{
|
||||
mach_header_t *mH;
|
||||
uint32_t index;
|
||||
addr_t cmdBase;
|
||||
|
||||
mH = (mach_header_t *)imageAddr;
|
||||
cmdBase = (addr_t)(mH + 1);
|
||||
|
||||
*virtualBase = 0;
|
||||
*virtualEnd = 0;
|
||||
|
||||
gRelocBase = (addr_t)NULL;
|
||||
gLinkeditBase = (addr_t)NULL;
|
||||
gLinkeditOffset = 0;
|
||||
|
||||
dprintf(DEBUG_INFO, "mach-o: loading %p->%p\n", (void *)imageAddr, (void *)loadAddr);
|
||||
dprintf(DEBUG_INFO, " magic 0x%x\n", mH->magic);
|
||||
dprintf(DEBUG_INFO, " cputype 0x%x\n", mH->cputype);
|
||||
dprintf(DEBUG_INFO, " cpusubtype 0x%x\n", mH->cpusubtype);
|
||||
dprintf(DEBUG_INFO, " filetype 0x%x\n", mH->filetype);
|
||||
dprintf(DEBUG_INFO, " ncmds 0x%x\n", mH->ncmds);
|
||||
dprintf(DEBUG_INFO, " sizeofcmds 0x%x\n", mH->sizeofcmds);
|
||||
dprintf(DEBUG_INFO, " flags 0x%x\n", mH->flags);
|
||||
|
||||
for (index = 0; index < mH->ncmds; index++) {
|
||||
switch(((struct load_command *)cmdBase)->cmd) {
|
||||
#if defined(__arm__) || defined(__arm64__)
|
||||
case LC_SEGMENT_ARM:
|
||||
dprintf(gkalsr_debug, "cmd LC_SEGMENT_ARM:\n");
|
||||
#else
|
||||
# error no support for this architecture
|
||||
#endif
|
||||
if (!macho_load_segment(imageAddr, imageSize, cmdBase, loadAddr, virtualBase, virtualEnd, slide))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case LC_UNIXTHREAD:
|
||||
dprintf(gkalsr_debug, "cmd LC_UNIXTHREAD:\n");
|
||||
// plus 8 for flavor and count fields
|
||||
#if defined(__arm__) || defined(__arm64__)
|
||||
*entryPoint = ((arm_thread_state_t *)(cmdBase + sizeof(struct thread_command) + 8))->pc;
|
||||
#else
|
||||
# error no support for this architecture
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
||||
case LC_SYMTAB:
|
||||
dprintf(gkalsr_debug, "cmd LC_SYMTAB:\n");
|
||||
if (slide == 0) {
|
||||
break;
|
||||
}
|
||||
if (!ProcessSymbols(cmdBase, slide))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case LC_DYSYMTAB:
|
||||
dprintf(gkalsr_debug, "cmd LC_DYSYMTAB:\n");
|
||||
if (slide == 0) {
|
||||
break;
|
||||
}
|
||||
if (!ProcessRelocations(cmdBase, slide))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
cmdBase += ((struct load_command *)cmdBase)->cmdsize;
|
||||
}
|
||||
|
||||
if (slide != 0) {
|
||||
*entryPoint = *entryPoint + slide;
|
||||
*virtualBase = *virtualBase + slide;
|
||||
*virtualEnd = *virtualEnd + slide;
|
||||
}
|
||||
|
||||
dprintf(gkalsr_debug, "macho_load: *entryPoint %p *virtualBase %p *virtualEnd %p\n",
|
||||
(void *)*entryPoint, (void *)*virtualBase, (void *)*virtualEnd);
|
||||
|
||||
/*
|
||||
* Clean everything we have just written out to memory.
|
||||
*
|
||||
* XXX this should really not be necessary...
|
||||
*/
|
||||
platform_cache_operation(CACHE_CLEAN, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process a Mach-o LC_SEGMENT/LC_SEGMENT_64 command.
|
||||
*/
|
||||
static bool
|
||||
macho_load_segment(
|
||||
addr_t imageAddr,
|
||||
size_t imageSize,
|
||||
addr_t segmentAddr,
|
||||
addr_t loadAddr,
|
||||
addr_t *virtualBase,
|
||||
addr_t *virtualEnd,
|
||||
size_t slide)
|
||||
{
|
||||
segment_command_t *cmd;
|
||||
addr_t segmentSourceAddress;
|
||||
addr_t segmentDestinationAddress;
|
||||
addr_t segmentVirtualEnd;
|
||||
size_t segmentLoadSize;
|
||||
size_t segmentFillSize;
|
||||
|
||||
cmd = (segment_command_t *)segmentAddr;
|
||||
|
||||
/*
|
||||
* We assume that the first segment in the object
|
||||
* is lowest in memory, and round it down to the
|
||||
* nearest megabyte.
|
||||
*/
|
||||
if (0 == *virtualBase) {
|
||||
#if defined(__LP64__)
|
||||
*virtualBase = cmd->vmaddr & 0xfffffffffff00000ULL;
|
||||
#else
|
||||
*virtualBase = cmd->vmaddr & 0xfff00000UL;
|
||||
#endif
|
||||
} else {
|
||||
/* sanity check the segment */
|
||||
if (cmd->vmaddr < *virtualBase)
|
||||
return false;
|
||||
if ((cmd->vmaddr + cmd->vmsize) < *virtualBase)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* XXX should add checks for the file*
|
||||
* members and sizes
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Mostly ignore the __PAGEZERO segment.
|
||||
*/
|
||||
if (!strcmp(cmd->segname, "__PAGEZERO"))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Precalculate various things we are going to
|
||||
* need to refer to as we copy and fill the segment.
|
||||
*/
|
||||
segmentSourceAddress = imageAddr + cmd->fileoff;
|
||||
segmentDestinationAddress = loadAddr + (cmd->vmaddr - *virtualBase);
|
||||
segmentLoadSize = __min(cmd->vmsize, cmd->filesize);
|
||||
segmentFillSize = __min(cmd->vmsize, cmd->vmsize - cmd->filesize);
|
||||
segmentVirtualEnd = cmd->vmaddr + cmd->vmsize;
|
||||
|
||||
dprintf(DEBUG_INFO,
|
||||
" %-16.16s %p/%p/%p 0x%zx+0x%zx\n",
|
||||
cmd->segname,
|
||||
(void *)segmentDestinationAddress,
|
||||
(void *)cmd->vmaddr,
|
||||
(void *)cmd->vmaddr + slide,
|
||||
segmentLoadSize,
|
||||
segmentFillSize);
|
||||
|
||||
/*
|
||||
* Copy the portion of the segment contained in
|
||||
* the object to its location in the loaded area.
|
||||
*/
|
||||
if (segmentLoadSize) {
|
||||
addr_t segmentEnd = segmentDestinationAddress + segmentLoadSize;
|
||||
/* no integer overflow */
|
||||
RELEASE_ASSERT(segmentEnd > segmentDestinationAddress);
|
||||
/* don't overwrite the image we're loading from; assumes we always load
|
||||
* to an address below the image we're loading from
|
||||
*/
|
||||
RELEASE_ASSERT((segmentEnd <= imageAddr) || (segmentEnd >= imageAddr + imageSize));
|
||||
|
||||
memcpy((void *)segmentDestinationAddress,
|
||||
(void *)segmentSourceAddress,
|
||||
segmentLoadSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Zero the remaining portion of the segment.
|
||||
*/
|
||||
if (segmentFillSize) {
|
||||
addr_t fillEnd = segmentDestinationAddress + segmentLoadSize + segmentFillSize;
|
||||
/* no integer overflow */
|
||||
RELEASE_ASSERT(fillEnd > segmentDestinationAddress);
|
||||
/* don't overwrite the image we're loading from; assumes we always load
|
||||
* to an address below the image we're loading from
|
||||
*/
|
||||
RELEASE_ASSERT((fillEnd <= imageAddr) || (fillEnd >= imageAddr + imageSize));
|
||||
|
||||
memset((void *)(segmentDestinationAddress + segmentLoadSize),
|
||||
0,
|
||||
segmentFillSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the occupied length if we have extended it.
|
||||
*/
|
||||
if (segmentVirtualEnd > *virtualEnd)
|
||||
*virtualEnd = segmentVirtualEnd;
|
||||
|
||||
if (slide != 0) {
|
||||
// Remember reloc and linkedit base
|
||||
if (gRelocBase == (addr_t)NULL) {
|
||||
gRelocBase = (addr_t)segmentDestinationAddress;
|
||||
dprintf(gkalsr_debug,"KASLR: gRelocBase = %p\n", (void *)gRelocBase);
|
||||
}
|
||||
|
||||
if ((gLinkeditBase == (addr_t)NULL) && (!strcmp(cmd->segname, "__LINKEDIT"))) {
|
||||
gLinkeditBase = (addr_t)segmentDestinationAddress;
|
||||
gLinkeditOffset = (uint32_t)(cmd->fileoff);
|
||||
dprintf(gkalsr_debug,"KASR: gLinkeditBase = %p gLinkeditOffset = 0x%x\n", (void *)gLinkeditBase, gLinkeditOffset);
|
||||
}
|
||||
|
||||
if (strcmp(cmd->segname, "__TEXT") == 0) {
|
||||
segment_command_t *seg;
|
||||
section_t *sec;
|
||||
mach_header_t *mh = (mach_header_t *)segmentDestinationAddress;
|
||||
|
||||
dprintf(gkalsr_debug, "Applying KASLR slide to _mh_execute_header contents:\n");
|
||||
for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
|
||||
|
||||
dprintf(gkalsr_debug," segment %s: %p -> %p\n", seg->segname,
|
||||
(void *)seg->vmaddr, (void *)(seg->vmaddr + slide));
|
||||
seg->vmaddr += slide;
|
||||
|
||||
for (sec = firstsect(seg); sec != NULL; sec = nextsect(seg, sec)) {
|
||||
dprintf(gkalsr_debug," -> section %s: %p -> %p\n", sec->sectname,
|
||||
(void *)sec->addr, (void *)(sec->addr + slide));
|
||||
sec->addr += slide;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse segment sections
|
||||
*/
|
||||
{
|
||||
segment_command_t *seg;
|
||||
section_t *sec;
|
||||
addr_t sectionDestinationAddress;
|
||||
|
||||
seg = (segment_command_t *)segmentAddr;
|
||||
for (sec = firstsect(seg); sec != NULL; sec = nextsect(seg, sec)) {
|
||||
sectionDestinationAddress = loadAddr + (sec->addr - *virtualBase);
|
||||
|
||||
dprintf(gkalsr_debug," -> section %s: %p/%p 0x%llx\n",
|
||||
sec->sectname, (void *)sectionDestinationAddress, (void *)sec->addr, (uint64_t)sec->size);
|
||||
/* Slide pointers in __nl_symbol_ptr section */
|
||||
if (((sec->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS)) {
|
||||
void **nl_symbols_ptr;
|
||||
dprintf(gkalsr_debug,"Slide S_NON_LAZY_SYMBOL_POINTERS \n");
|
||||
for (nl_symbols_ptr = (void **)sectionDestinationAddress;
|
||||
nl_symbols_ptr < (void **)(sectionDestinationAddress+sec->size);
|
||||
nl_symbols_ptr++) {
|
||||
if (nl_symbols_ptr == (void **)sectionDestinationAddress)
|
||||
dprintf(gkalsr_debug," %p : %p -> %p\n",
|
||||
nl_symbols_ptr, *nl_symbols_ptr, *nl_symbols_ptr + slide);
|
||||
*nl_symbols_ptr = *nl_symbols_ptr + slide;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the first segment_command in the header.
|
||||
*/
|
||||
segment_command_t *
|
||||
firstsegfromheader(mach_header_t *header)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
segment_command_t *sgp = (segment_command_t *) ((uintptr_t)header + sizeof(*header));
|
||||
|
||||
for (i = 0; i < header->ncmds; i++){
|
||||
#if defined(__arm__) || defined(__arm64__)
|
||||
if (sgp->cmd == LC_SEGMENT_ARM)
|
||||
return sgp;
|
||||
#else
|
||||
# error no support for this architecture
|
||||
#endif
|
||||
sgp = (segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
|
||||
}
|
||||
return (segment_command_t *)NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine operates against any kernel mach segment_command structure
|
||||
* pointer and the provided kernel header, to obtain the sequentially next
|
||||
* segment_command structure in that header.
|
||||
*/
|
||||
segment_command_t *
|
||||
nextsegfromheader(mach_header_t *header,
|
||||
segment_command_t *seg)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
segment_command_t *sgp = (segment_command_t *) ((uintptr_t)header + sizeof(*header));
|
||||
|
||||
/* Find the index of the passed-in segment */
|
||||
for (i = 0; sgp != seg && i < header->ncmds; i++) {
|
||||
sgp = (segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
|
||||
}
|
||||
|
||||
/* Increment to the next load command */
|
||||
i++;
|
||||
sgp = (segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
|
||||
|
||||
/* Return the next segment command, if any */
|
||||
for (; i < header->ncmds; i++) {
|
||||
#if defined(__arm__) || defined(__arm64__)
|
||||
if (sgp->cmd == LC_SEGMENT_ARM)
|
||||
return sgp;
|
||||
#else
|
||||
# error no support for this architecture
|
||||
#endif
|
||||
|
||||
sgp = (segment_command_t *)((uintptr_t)sgp + sgp->cmdsize);
|
||||
}
|
||||
|
||||
return (segment_command_t *)NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine can operate against any kernel segment_command structure to
|
||||
* return the first kernel section immediately following that structure. If
|
||||
* there are no sections associated with the segment_command structure, it
|
||||
* returns NULL.
|
||||
*/
|
||||
section_t *
|
||||
firstsect(segment_command_t *sgp)
|
||||
{
|
||||
if (!sgp || sgp->nsects == 0)
|
||||
return (section_t *)NULL;
|
||||
|
||||
return (section_t *)(sgp + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine can operate against any kernel segment_command structure and
|
||||
* kernel section to return the next consecutive kernel section immediately
|
||||
* following the kernel section provided. If there are no sections following
|
||||
* the provided section, it returns NULL.
|
||||
*/
|
||||
section_t *
|
||||
nextsect(segment_command_t *sgp, section_t *sp)
|
||||
{
|
||||
section_t *fsp = firstsect(sgp);
|
||||
|
||||
if (((uintptr_t)(sp - fsp) + 1) >= sgp->nsects)
|
||||
return (section_t *)NULL;
|
||||
|
||||
return sp + 1;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessSymbols(addr_t cmdBase, size_t slide)
|
||||
{
|
||||
struct symtab_command *symtab;
|
||||
#if defined(__LP64__)
|
||||
struct nlist_64 *sym;
|
||||
#else
|
||||
struct nlist *sym;
|
||||
#endif
|
||||
uint32_t i;
|
||||
uint32_t cnt = 0;
|
||||
|
||||
symtab = (struct symtab_command *)cmdBase;
|
||||
dprintf(gkalsr_debug, "Symbols:\n");
|
||||
dprintf(gkalsr_debug, "nsyms: %d, symoff: 0x%x\n", symtab->nsyms, symtab->symoff);
|
||||
|
||||
if (symtab->nsyms == 0) {
|
||||
dprintf(gkalsr_debug, "No symbols to relocate\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__LP64__)
|
||||
sym = (struct nlist_64 *)(gLinkeditBase + symtab->symoff - gLinkeditOffset);
|
||||
#else
|
||||
sym = (struct nlist *)(gLinkeditBase + symtab->symoff - gLinkeditOffset);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < symtab->nsyms; i++) {
|
||||
if (sym[i].n_type & N_STAB)
|
||||
continue;
|
||||
sym[i].n_value += slide;
|
||||
cnt++;
|
||||
}
|
||||
dprintf(gkalsr_debug, "KASLR: Relocated %d symbols\n", cnt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessRelocations(addr_t cmdBase, size_t slide)
|
||||
{
|
||||
struct dysymtab_command *dysymtab;
|
||||
|
||||
dysymtab = (struct dysymtab_command *)cmdBase;
|
||||
|
||||
dprintf(gkalsr_debug, "Symbols:\n");
|
||||
dprintf(gkalsr_debug, "local: %6d @ 0x%x\n", dysymtab->nlocalsym, dysymtab->ilocalsym);
|
||||
dprintf(gkalsr_debug, "external: %6d @ 0x%x\n", dysymtab->nextdefsym, dysymtab->iextdefsym);
|
||||
dprintf(gkalsr_debug, "undefined: %6d @ 0x%x\n", dysymtab->nundefsym, dysymtab->iundefsym);
|
||||
dprintf(gkalsr_debug, "indirect: %6d @ 0x%x\n", dysymtab->nindirectsyms, dysymtab->indirectsymoff);
|
||||
|
||||
dprintf(gkalsr_debug, "Relocation entries:\n");
|
||||
dprintf(gkalsr_debug, "local: %6d @ 0x%x\n", dysymtab->nlocrel, dysymtab->locreloff);
|
||||
dprintf(gkalsr_debug, "external: %6d @ 0x%x\n", dysymtab->nextrel, dysymtab->extreloff);
|
||||
|
||||
if (dysymtab->nlocrel != 0) {
|
||||
struct relocation_info *relocStart;
|
||||
struct relocation_info *relocEnd;
|
||||
struct relocation_info *reloc;
|
||||
|
||||
if (gRelocBase == (addr_t)NULL) {
|
||||
dprintf(gkalsr_debug, "KASLR: no writeable segment for relocations found\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gLinkeditBase == (addr_t)NULL) {
|
||||
dprintf(gkalsr_debug, "KASLR: No __LINKEDIT segment found\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
relocStart = (struct relocation_info *)(gLinkeditBase + dysymtab->locreloff - gLinkeditOffset);
|
||||
relocEnd = &relocStart[dysymtab->nlocrel];
|
||||
|
||||
dprintf(gkalsr_debug, "KASLR: processing local relocations...\n");
|
||||
dprintf(gkalsr_debug, "relocStart = %p, relocEnd = %p\n", relocStart, relocEnd);
|
||||
|
||||
for (reloc = relocStart; reloc < relocEnd; reloc++) {
|
||||
if ((1 << reloc->r_length) != sizeof(void *)) {
|
||||
/* 2 to the r_length should be the size of a pointer for all relocations */
|
||||
dprintf(gkalsr_debug, "bad local relocation length (%d)\n", reloc->r_length);
|
||||
return false;
|
||||
}
|
||||
if (reloc->r_type != ARM_RELOC_VANILLA) {
|
||||
dprintf(gkalsr_debug, "unknown local relocation type (%d)\n", reloc->r_type);
|
||||
return false;
|
||||
}
|
||||
if (reloc->r_pcrel != 0) {
|
||||
dprintf(gkalsr_debug, "bad local relocation r_pcrel");
|
||||
return false;
|
||||
}
|
||||
if (reloc->r_extern != 0) {
|
||||
dprintf(gkalsr_debug, "extern relocation found with local relocations\n");
|
||||
return false;
|
||||
}
|
||||
*((uintptr_t*)(gRelocBase + reloc->r_address)) += slide;
|
||||
|
||||
if (reloc == relocStart)
|
||||
dprintf(gkalsr_debug, "r_address = 0x%x, gRelocBase + reloc->r_addres = %p\n",
|
||||
reloc->r_address, (uintptr_t*)(gRelocBase + reloc->r_address));
|
||||
}
|
||||
dprintf(gkalsr_debug, "KASLR: %d local relocations processed\n", dysymtab->nlocrel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2011 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 1998-2006 Apple Computer, 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.
|
||||
*/
|
||||
|
||||
#ifndef __MACHO_HEADER_H
|
||||
#define __MACHO_HEADER_H
|
||||
|
||||
// Taken from osfmk/mach/vm_prot.h
|
||||
typedef int vm_prot_t;
|
||||
#define VM_PROT_NONE ((vm_prot_t) 0x00)
|
||||
#define VM_PROT_READ ((vm_prot_t) 0x01) /* read permission */
|
||||
#define VM_PROT_WRITE ((vm_prot_t) 0x02) /* write permission */
|
||||
#define VM_PROT_EXECUTE ((vm_prot_t) 0x04) /* execute permission */
|
||||
|
||||
|
||||
// Taken from osfmk/mach/arm/vm_types.h
|
||||
typedef int integer_t;
|
||||
|
||||
// Taken from osfmk/mach/machine.h
|
||||
|
||||
typedef integer_t cpu_type_t;
|
||||
typedef integer_t cpu_subtype_t;
|
||||
|
||||
#ifndef __LP64__
|
||||
// Taken from mach-o/loader.h
|
||||
|
||||
/*
|
||||
* The 32-bit mach header appears at the very beginning of the object file for
|
||||
* 32-bit architectures.
|
||||
*/
|
||||
typedef struct mach_header {
|
||||
uint32_t magic; /* mach magic number identifier */
|
||||
cpu_type_t cputype; /* cpu specifier */
|
||||
cpu_subtype_t cpusubtype; /* machine specifier */
|
||||
uint32_t filetype; /* type of file */
|
||||
uint32_t ncmds; /* number of load commands */
|
||||
uint32_t sizeofcmds; /* the size of all the load commands */
|
||||
uint32_t flags; /* flags */
|
||||
}mach_header_t;
|
||||
|
||||
/* Constant for the magic field of the mach_header (32-bit architectures) */
|
||||
#define MH_MAGIC 0xfeedface /* the mach magic number */
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* The 64-bit mach header appears at the very beginning of object files for
|
||||
* 64-bit architectures.
|
||||
*/
|
||||
typedef struct mach_header_64 {
|
||||
uint32_t magic; /* mach magic number identifier */
|
||||
cpu_type_t cputype; /* cpu specifier */
|
||||
cpu_subtype_t cpusubtype; /* machine specifier */
|
||||
uint32_t filetype; /* type of file */
|
||||
uint32_t ncmds; /* number of load commands */
|
||||
uint32_t sizeofcmds; /* the size of all the load commands */
|
||||
uint32_t flags; /* flags */
|
||||
uint32_t reserved; /* reserved */
|
||||
} mach_header_t;
|
||||
|
||||
/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
|
||||
#define MH_MAGIC 0xfeedfacf /* the 64-bit mach magic number */
|
||||
|
||||
#endif
|
||||
|
||||
/* Constants for the flags field of the mach_header */
|
||||
#define MH_PIE 0x200000 /* When this bit is set, the OS will
|
||||
load the main executable at a
|
||||
random address. Only used in
|
||||
MH_EXECUTE filetypes. */
|
||||
|
||||
/*
|
||||
* The load commands directly follow the mach_header. The total size of all
|
||||
* of the commands is given by the sizeofcmds field in the mach_header. All
|
||||
* load commands must have as their first two fields cmd and cmdsize. The cmd
|
||||
* field is filled in with a constant for that command type. Each command type
|
||||
* has a structure specifically for it. The cmdsize field is the size in bytes
|
||||
* of the particular load command structure plus anything that follows it that
|
||||
* is a part of the load command (i.e. section structures, strings, etc.). To
|
||||
* advance to the next load command the cmdsize can be added to the offset or
|
||||
* pointer of the current load command. The cmdsize for 32-bit architectures
|
||||
* MUST be a multiple of 4 bytes and for 64-bit architectures MUST be a multiple
|
||||
* of 8 bytes (these are forever the maximum alignment of any load commands).
|
||||
* The padded bytes must be zero. All tables in the object file must also
|
||||
* follow these rules so the file can be memory mapped. Otherwise the pointers
|
||||
* to these tables will not work well or at all on some machines. With all
|
||||
* padding zeroed like objects will compare byte for byte.
|
||||
*/
|
||||
struct load_command {
|
||||
uint32_t cmd; /* type of load command */
|
||||
uint32_t cmdsize; /* total size of command in bytes */
|
||||
};
|
||||
|
||||
/* Constants for the cmd field of all load commands, the type */
|
||||
#define LC_SEGMENT 0x1 /* segment of this file to be mapped */
|
||||
#define LC_SYMTAB 0x2 /* link-edit stab symbol table info */
|
||||
#define LC_THREAD 0x4 /* thread */
|
||||
#define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */
|
||||
#define LC_DYSYMTAB 0xb /* dynamic link-edit symbol table info */
|
||||
#define LC_SEGMENT_64 0x19 /* 64-bit segment of this file to be mapped */
|
||||
|
||||
#ifndef __LP64__
|
||||
#define LC_SEGMENT_ARM LC_SEGMENT
|
||||
#else
|
||||
#define LC_SEGMENT_ARM LC_SEGMENT_64
|
||||
#endif
|
||||
|
||||
#ifndef __LP64__
|
||||
/*
|
||||
* The segment load command indicates that a part of this file is to be
|
||||
* mapped into the task's address space. The size of this segment in memory,
|
||||
* vmsize, maybe equal to or larger than the amount to map from this file,
|
||||
* filesize. The file is mapped starting at fileoff to the beginning of
|
||||
* the segment in memory, vmaddr. The rest of the memory of the segment,
|
||||
* if any, is allocated zero fill on demand. The segment's maximum virtual
|
||||
* memory protection and initial virtual memory protection are specified
|
||||
* by the maxprot and initprot fields. If the segment has sections then the
|
||||
* section structures directly follow the segment command and their size is
|
||||
* reflected in cmdsize.
|
||||
*/
|
||||
typedef struct segment_command { /* for 32-bit architectures */
|
||||
uint32_t cmd; /* LC_SEGMENT */
|
||||
uint32_t cmdsize; /* includes sizeof section structs */
|
||||
char segname[16]; /* segment name */
|
||||
uint32_t vmaddr; /* memory address of this segment */
|
||||
uint32_t vmsize; /* memory size of this segment */
|
||||
uint32_t fileoff; /* file offset of this segment */
|
||||
uint32_t filesize; /* amount to map from the file */
|
||||
vm_prot_t maxprot; /* maximum VM protection */
|
||||
vm_prot_t initprot; /* initial VM protection */
|
||||
uint32_t nsects; /* number of sections in segment */
|
||||
uint32_t flags; /* flags */
|
||||
} segment_command_t;
|
||||
#else
|
||||
/*
|
||||
* The 64-bit segment load command indicates that a part of this file is to be
|
||||
* mapped into a 64-bit task's address space. If the 64-bit segment has
|
||||
* sections then section_64 structures directly follow the 64-bit segment
|
||||
* command and their size is reflected in cmdsize.
|
||||
*/
|
||||
typedef struct segment_command_64 { /* for 64-bit architectures */
|
||||
uint32_t cmd; /* LC_SEGMENT_64 */
|
||||
uint32_t cmdsize; /* includes sizeof section_64 structs */
|
||||
char segname[16]; /* segment name */
|
||||
uint64_t vmaddr; /* memory address of this segment */
|
||||
uint64_t vmsize; /* memory size of this segment */
|
||||
uint64_t fileoff; /* file offset of this segment */
|
||||
uint64_t filesize; /* amount to map from the file */
|
||||
vm_prot_t maxprot; /* maximum VM protection */
|
||||
vm_prot_t initprot; /* initial VM protection */
|
||||
uint32_t nsects; /* number of sections in segment */
|
||||
uint32_t flags; /* flags */
|
||||
} segment_command_t;
|
||||
#endif
|
||||
|
||||
#ifndef __LP64__
|
||||
typedef struct section { /* for 32-bit architectures */
|
||||
char sectname[16]; /* name of this section */
|
||||
char segname[16]; /* segment this section goes in */
|
||||
uint32_t addr; /* memory address of this section */
|
||||
uint32_t size; /* size in bytes of this section */
|
||||
uint32_t offset; /* file offset of this section */
|
||||
uint32_t align; /* section alignment (power of 2) */
|
||||
uint32_t reloff; /* file offset of relocation entries */
|
||||
uint32_t nreloc; /* number of relocation entries */
|
||||
uint32_t flags; /* flags (section type and attributes)*/
|
||||
uint32_t reserved1; /* reserved (for offset or index) */
|
||||
uint32_t reserved2; /* reserved (for count or sizeof) */
|
||||
} section_t;
|
||||
#else
|
||||
typedef struct section_64 { /* for 64-bit architectures */
|
||||
char sectname[16]; /* name of this section */
|
||||
char segname[16]; /* segment this section goes in */
|
||||
uint64_t addr; /* memory address of this section */
|
||||
uint64_t size; /* size in bytes of this section */
|
||||
uint32_t offset; /* file offset of this section */
|
||||
uint32_t align; /* section alignment (power of 2) */
|
||||
uint32_t reloff; /* file offset of relocation entries */
|
||||
uint32_t nreloc; /* number of relocation entries */
|
||||
uint32_t flags; /* flags (section type and attributes)*/
|
||||
uint32_t reserved1; /* reserved (for offset or index) */
|
||||
uint32_t reserved2; /* reserved (for count or sizeof) */
|
||||
uint32_t reserved3; /* reserved */
|
||||
} section_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The flags field of a section structure is separated into two parts a section
|
||||
* type and section attributes. The section types are mutually exclusive (it
|
||||
* can only have one type) but the section attributes are not (it may have more
|
||||
* than one attribute).
|
||||
*/
|
||||
#define SECTION_TYPE 0x000000ff /* 256 section types */
|
||||
#define SECTION_ATTRIBUTES 0xffffff00 /* 24 section attributes */
|
||||
|
||||
/* Constants for the type of a section */
|
||||
#define S_REGULAR 0x0 /* regular section */
|
||||
#define S_ZEROFILL 0x1 /* zero fill on demand section */
|
||||
#define S_CSTRING_LITERALS 0x2 /* section with only literal C strings*/
|
||||
#define S_4BYTE_LITERALS 0x3 /* section with only 4 byte literals */
|
||||
#define S_8BYTE_LITERALS 0x4 /* section with only 8 byte literals */
|
||||
#define S_LITERAL_POINTERS 0x5 /* section with only pointers to */
|
||||
/* literals */
|
||||
/*
|
||||
* For the two types of symbol pointers sections and the symbol stubs section
|
||||
* they have indirect symbol table entries. For each of the entries in the
|
||||
* section the indirect symbol table entries, in corresponding order in the
|
||||
* indirect symbol table, start at the index stored in the reserved1 field
|
||||
* of the section structure. Since the indirect symbol table entries
|
||||
* correspond to the entries in the section the number of indirect symbol table
|
||||
* entries is inferred from the size of the section divided by the size of the
|
||||
* entries in the section. For symbol pointers sections the size of the entries
|
||||
* in the section is 4 bytes and for symbol stubs sections the byte size of the
|
||||
* stubs is stored in the reserved2 field of the section structure.
|
||||
*/
|
||||
#define S_NON_LAZY_SYMBOL_POINTERS 0x6 /* section with only non-lazy
|
||||
symbol pointers */
|
||||
#define S_LAZY_SYMBOL_POINTERS 0x7 /* section with only lazy symbol
|
||||
pointers */
|
||||
#define S_SYMBOL_STUBS 0x8 /* section with only symbol
|
||||
stubs, byte size of stub in
|
||||
the reserved2 field */
|
||||
#define S_MOD_INIT_FUNC_POINTERS 0x9 /* section with only function
|
||||
pointers for initialization*/
|
||||
#define S_MOD_TERM_FUNC_POINTERS 0xa /* section with only function
|
||||
pointers for termination */
|
||||
#define S_COALESCED 0xb /* section contains symbols that
|
||||
are to be coalesced */
|
||||
#define S_GB_ZEROFILL 0xc /* zero fill on demand section
|
||||
(that can be larger than 4
|
||||
gigabytes) */
|
||||
#define S_INTERPOSING 0xd /* section with only pairs of
|
||||
function pointers for
|
||||
interposing */
|
||||
#define S_16BYTE_LITERALS 0xe /* section with only 16 byte
|
||||
literals */
|
||||
#define S_DTRACE_DOF 0xf /* section contains
|
||||
DTrace Object Format */
|
||||
#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 /* section with only lazy
|
||||
symbol pointers to lazy
|
||||
loaded dylibs */
|
||||
|
||||
|
||||
/*
|
||||
* The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
|
||||
* "stab" style symbol table information as described in the header files
|
||||
* <nlist.h> and <stab.h>.
|
||||
*/
|
||||
struct symtab_command {
|
||||
uint32_t cmd; /* LC_SYMTAB */
|
||||
uint32_t cmdsize; /* sizeof(struct symtab_command) */
|
||||
uint32_t symoff; /* symbol table offset */
|
||||
uint32_t nsyms; /* number of symbol table entries */
|
||||
uint32_t stroff; /* string table offset */
|
||||
uint32_t strsize; /* string table size in bytes */
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the second set of the symbolic information which is used to support
|
||||
* the data structures for the dynamically link editor.
|
||||
*
|
||||
* The original set of symbolic information in the symtab_command which contains
|
||||
* the symbol and string tables must also be present when this load command is
|
||||
* present. When this load command is present the symbol table is organized
|
||||
* into three groups of symbols:
|
||||
* local symbols (static and debugging symbols) - grouped by module
|
||||
* defined external symbols - grouped by module (sorted by name if not lib)
|
||||
* undefined external symbols (sorted by name if MH_BINDATLOAD is not set,
|
||||
* and in order the were seen by the static
|
||||
* linker if MH_BINDATLOAD is set)
|
||||
* In this load command there are offsets and counts to each of the three groups
|
||||
* of symbols.
|
||||
*
|
||||
* This load command contains a the offsets and sizes of the following new
|
||||
* symbolic information tables:
|
||||
* table of contents
|
||||
* module table
|
||||
* reference symbol table
|
||||
* indirect symbol table
|
||||
* The first three tables above (the table of contents, module table and
|
||||
* reference symbol table) are only present if the file is a dynamically linked
|
||||
* shared library. For executable and object modules, which are files
|
||||
* containing only one module, the information that would be in these three
|
||||
* tables is determined as follows:
|
||||
* table of contents - the defined external symbols are sorted by name
|
||||
* module table - the file contains only one module so everything in the
|
||||
* file is part of the module.
|
||||
* reference symbol table - is the defined and undefined external symbols
|
||||
*
|
||||
* For dynamically linked shared library files this load command also contains
|
||||
* offsets and sizes to the pool of relocation entries for all sections
|
||||
* separated into two groups:
|
||||
* external relocation entries
|
||||
* local relocation entries
|
||||
* For executable and object modules the relocation entries continue to hang
|
||||
* off the section structures.
|
||||
*/
|
||||
struct dysymtab_command {
|
||||
uint32_t cmd; /* LC_DYSYMTAB */
|
||||
uint32_t cmdsize; /* sizeof(struct dysymtab_command) */
|
||||
|
||||
/*
|
||||
* The symbols indicated by symoff and nsyms of the LC_SYMTAB load command
|
||||
* are grouped into the following three groups:
|
||||
* local symbols (further grouped by the module they are from)
|
||||
* defined external symbols (further grouped by the module they are from)
|
||||
* undefined symbols
|
||||
*
|
||||
* The local symbols are used only for debugging. The dynamic binding
|
||||
* process may have to use them to indicate to the debugger the local
|
||||
* symbols for a module that is being bound.
|
||||
*
|
||||
* The last two groups are used by the dynamic binding process to do the
|
||||
* binding (indirectly through the module table and the reference symbol
|
||||
* table when this is a dynamically linked shared library file).
|
||||
*/
|
||||
uint32_t ilocalsym; /* index to local symbols */
|
||||
uint32_t nlocalsym; /* number of local symbols */
|
||||
|
||||
uint32_t iextdefsym;/* index to externally defined symbols */
|
||||
uint32_t nextdefsym;/* number of externally defined symbols */
|
||||
|
||||
uint32_t iundefsym; /* index to undefined symbols */
|
||||
uint32_t nundefsym; /* number of undefined symbols */
|
||||
|
||||
/*
|
||||
* For the for the dynamic binding process to find which module a symbol
|
||||
* is defined in the table of contents is used (analogous to the ranlib
|
||||
* structure in an archive) which maps defined external symbols to modules
|
||||
* they are defined in. This exists only in a dynamically linked shared
|
||||
* library file. For executable and object modules the defined external
|
||||
* symbols are sorted by name and is use as the table of contents.
|
||||
*/
|
||||
uint32_t tocoff; /* file offset to table of contents */
|
||||
uint32_t ntoc; /* number of entries in table of contents */
|
||||
|
||||
/*
|
||||
* To support dynamic binding of "modules" (whole object files) the symbol
|
||||
* table must reflect the modules that the file was created from. This is
|
||||
* done by having a module table that has indexes and counts into the merged
|
||||
* tables for each module. The module structure that these two entries
|
||||
* refer to is described below. This exists only in a dynamically linked
|
||||
* shared library file. For executable and object modules the file only
|
||||
* contains one module so everything in the file belongs to the module.
|
||||
*/
|
||||
uint32_t modtaboff; /* file offset to module table */
|
||||
uint32_t nmodtab; /* number of module table entries */
|
||||
|
||||
/*
|
||||
* To support dynamic module binding the module structure for each module
|
||||
* indicates the external references (defined and undefined) each module
|
||||
* makes. For each module there is an offset and a count into the
|
||||
* reference symbol table for the symbols that the module references.
|
||||
* This exists only in a dynamically linked shared library file. For
|
||||
* executable and object modules the defined external symbols and the
|
||||
* undefined external symbols indicates the external references.
|
||||
*/
|
||||
uint32_t extrefsymoff; /* offset to referenced symbol table */
|
||||
uint32_t nextrefsyms; /* number of referenced symbol table entries */
|
||||
|
||||
/*
|
||||
* The sections that contain "symbol pointers" and "routine stubs" have
|
||||
* indexes and (implied counts based on the size of the section and fixed
|
||||
* size of the entry) into the "indirect symbol" table for each pointer
|
||||
* and stub. For every section of these two types the index into the
|
||||
* indirect symbol table is stored in the section header in the field
|
||||
* reserved1. An indirect symbol table entry is simply a 32bit index into
|
||||
* the symbol table to the symbol that the pointer or stub is referring to.
|
||||
* The indirect symbol table is ordered to match the entries in the section.
|
||||
*/
|
||||
uint32_t indirectsymoff; /* file offset to the indirect symbol table */
|
||||
uint32_t nindirectsyms; /* number of indirect symbol table entries */
|
||||
|
||||
/*
|
||||
* To support relocating an individual module in a library file quickly the
|
||||
* external relocation entries for each module in the library need to be
|
||||
* accessed efficiently. Since the relocation entries can't be accessed
|
||||
* through the section headers for a library file they are separated into
|
||||
* groups of local and external entries further grouped by module. In this
|
||||
* case the presents of this load command who's extreloff, nextrel,
|
||||
* locreloff and nlocrel fields are non-zero indicates that the relocation
|
||||
* entries of non-merged sections are not referenced through the section
|
||||
* structures (and the reloff and nreloc fields in the section headers are
|
||||
* set to zero).
|
||||
*
|
||||
* Since the relocation entries are not accessed through the section headers
|
||||
* this requires the r_address field to be something other than a section
|
||||
* offset to identify the item to be relocated. In this case r_address is
|
||||
* set to the offset from the vmaddr of the first LC_SEGMENT command.
|
||||
* For MH_SPLIT_SEGS images r_address is set to the the offset from the
|
||||
* vmaddr of the first read-write LC_SEGMENT command.
|
||||
*
|
||||
* The relocation entries are grouped by module and the module table
|
||||
* entries have indexes and counts into them for the group of external
|
||||
* relocation entries for that the module.
|
||||
*
|
||||
* For sections that are merged across modules there must not be any
|
||||
* remaining external relocation entries for them (for merged sections
|
||||
* remaining relocation entries must be local).
|
||||
*/
|
||||
uint32_t extreloff; /* offset to external relocation entries */
|
||||
uint32_t nextrel; /* number of external relocation entries */
|
||||
|
||||
/*
|
||||
* All the local relocation entries are grouped together (they are not
|
||||
* grouped by their module since they are only used if the object is moved
|
||||
* from it staticly link edited address).
|
||||
*/
|
||||
uint32_t locreloff; /* offset to local relocation entries */
|
||||
uint32_t nlocrel; /* number of local relocation entries */
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* An indirect symbol table entry is simply a 32bit index into the symbol table
|
||||
* to the symbol that the pointer or stub is refering to. Unless it is for a
|
||||
* non-lazy symbol pointer section for a defined symbol which strip(1) as
|
||||
* removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the
|
||||
* symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.
|
||||
*/
|
||||
#define INDIRECT_SYMBOL_LOCAL 0x80000000
|
||||
#define INDIRECT_SYMBOL_ABS 0x40000000
|
||||
|
||||
/*
|
||||
* Thread commands contain machine-specific data structures suitable for
|
||||
* use in the thread state primitives. The machine specific data structures
|
||||
* follow the struct thread_command as follows.
|
||||
* Each flavor of machine specific data structure is preceded by an unsigned
|
||||
* long constant for the flavor of that data structure, an uint32_t
|
||||
* that is the count of longs of the size of the state data structure and then
|
||||
* the state data structure follows. This triple may be repeated for many
|
||||
* flavors. The constants for the flavors, counts and state data structure
|
||||
* definitions are expected to be in the header file <machine/thread_status.h>.
|
||||
* These machine specific data structures sizes must be multiples of
|
||||
* 4 bytes The cmdsize reflects the total size of the thread_command
|
||||
* and all of the sizes of the constants for the flavors, counts and state
|
||||
* data structures.
|
||||
*
|
||||
* For executable objects that are unix processes there will be one
|
||||
* thread_command (cmd == LC_UNIXTHREAD) created for it by the link-editor.
|
||||
* This is the same as a LC_THREAD, except that a stack is automatically
|
||||
* created (based on the shell's limit for the stack size). Command arguments
|
||||
* and environment variables are copied onto that stack.
|
||||
*/
|
||||
struct thread_command {
|
||||
uint32_t cmd; /* LC_THREAD or LC_UNIXTHREAD */
|
||||
uint32_t cmdsize; /* total size of this command */
|
||||
/* uint32_t flavor flavor of thread state */
|
||||
/* uint32_t count count of longs in thread state */
|
||||
/* struct XXX_thread_state state thread state for this flavor */
|
||||
/* ... */
|
||||
};
|
||||
|
||||
|
||||
#ifdef __arm__
|
||||
// Taken from osfmk/mach/arm/_types.h
|
||||
|
||||
typedef struct arm_thread_state
|
||||
{
|
||||
uint32_t r[13]; /* General purpose register r0-r12 */
|
||||
uint32_t sp; /* Stack pointer r13 */
|
||||
uint32_t lr; /* Link regisster r14 */
|
||||
uint32_t pc; /* Program counter r15 */
|
||||
uint32_t cpsr; /* Current program status register */
|
||||
} arm_thread_state_t;
|
||||
#else
|
||||
#ifdef __arm64__
|
||||
// Based from osfmk/mach/arm/_structs.h
|
||||
|
||||
typedef struct arm_thread_state64
|
||||
{
|
||||
uint64_t x[29]; /* General purpose registers x0-x28 */
|
||||
uint64_t fp; /* Frame pointer x29 */
|
||||
uint64_t lr; /* Link register x30 */
|
||||
uint64_t sp; /* Stack pointer x31 */
|
||||
uint64_t pc; /* Program counter */
|
||||
uint32_t cpsr; /* Current program status register */
|
||||
} arm_thread_state_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Format of a symbol table entry of a Mach-O file for 32-bit architectures.
|
||||
* Modified from the BSD format. The modifications from the original format
|
||||
* were changing n_other (an unused field) to n_sect and the addition of the
|
||||
* N_SECT type. These modifications are required to support symbols in a larger
|
||||
* number of sections not just the three sections (text, data and bss) in a BSD
|
||||
* file.
|
||||
*/
|
||||
|
||||
struct nlist {
|
||||
union {
|
||||
#ifndef __LP64__
|
||||
char *n_name; /* for use when in-core */
|
||||
#endif
|
||||
int32_t n_strx; /* index into the string table */
|
||||
} n_un;
|
||||
uint8_t n_type; /* type flag, see below */
|
||||
uint8_t n_sect; /* section number or NO_SECT */
|
||||
int16_t n_desc; /* see <mach-o/stab.h> */
|
||||
uint32_t n_value; /* value of this symbol (or stab offset) */
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the symbol table entry structure for 64-bit architectures.
|
||||
*/
|
||||
struct nlist_64 {
|
||||
union {
|
||||
uint32_t n_strx; /* index into the string table */
|
||||
} n_un;
|
||||
uint8_t n_type; /* type flag, see below */
|
||||
uint8_t n_sect; /* section number or NO_SECT */
|
||||
uint16_t n_desc; /* see <mach-o/stab.h> */
|
||||
uint64_t n_value; /* value of this symbol (or stab offset) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Symbols with a index into the string table of zero (n_un.n_strx == 0) are
|
||||
* defined to have a null, "", name. Therefore all string indexes to non null
|
||||
* names must not have a zero string index. This is bit historical information
|
||||
* that has never been well documented.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The n_type field really contains four fields:
|
||||
* unsigned char N_STAB:3,
|
||||
* N_PEXT:1,
|
||||
* N_TYPE:3,
|
||||
* N_EXT:1;
|
||||
* which are used via the following masks.
|
||||
*/
|
||||
#define N_STAB 0xe0 /* if any of these bits set, a symbolic debugging entry */
|
||||
#define N_PEXT 0x10 /* private external symbol bit */
|
||||
#define N_TYPE 0x0e /* mask for the type bits */
|
||||
#define N_EXT 0x01 /* external symbol bit, set for external symbols */
|
||||
|
||||
|
||||
/*
|
||||
* Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD
|
||||
* format. The modifications from the original format were changing the value
|
||||
* of the r_symbolnum field for "local" (r_extern == 0) relocation entries.
|
||||
* This modification is required to support symbols in an arbitrary number of
|
||||
* sections not just the three sections (text, data and bss) in a 4.3BSD file.
|
||||
* Also the last 4 bits have had the r_type tag added to them.
|
||||
*/
|
||||
struct relocation_info {
|
||||
int32_t r_address; /* offset in the section to what is being
|
||||
relocated */
|
||||
uint32_t r_symbolnum:24, /* symbol index if r_extern == 1 or section
|
||||
ordinal if r_extern == 0 */
|
||||
r_pcrel:1, /* was relocated pc relative already */
|
||||
r_length:2, /* 0=byte, 1=word, 2=long, 3=quad */
|
||||
r_extern:1, /* does not include value of sym referenced */
|
||||
r_type:4; /* if not 0, machine specific relocation type */
|
||||
};
|
||||
|
||||
enum reloc_type_arm
|
||||
{
|
||||
ARM_RELOC_VANILLA, /* generic relocation as discribed above */
|
||||
ARM_RELOC_PAIR, /* the second relocation entry of a pair */
|
||||
ARM_RELOC_SECTDIFF, /* a PAIR follows with subtract symbol value */
|
||||
ARM_RELOC_LOCAL_SECTDIFF, /* like ARM_RELOC_SECTDIFF, but the symbol
|
||||
referenced was local. */
|
||||
ARM_RELOC_PB_LA_PTR,/* prebound lazy pointer */
|
||||
ARM_RELOC_BR24, /* 24 bit branch displacement (to a word address) */
|
||||
ARM_THUMB_RELOC_BR22, /* 22 bit branch displacement (to a half-word
|
||||
address) */
|
||||
ARM_THUMB_32BIT_BRANCH, /* obsolete - a thumb 32-bit branch instruction
|
||||
possibly needing page-spanning branch workaround */
|
||||
|
||||
/*
|
||||
* For these two r_type relocations they always have a pair following them
|
||||
* and the r_length bits are used differently. The encoding of the
|
||||
* r_length is as follows:
|
||||
* low bit of r_length:
|
||||
* 0 - :lower16: for movw instructions
|
||||
* 1 - :upper16: for movt instructions
|
||||
* high bit of r_length:
|
||||
* 0 - arm instructions
|
||||
* 1 - thumb instructions
|
||||
* the other half of the relocated expression is in the following pair
|
||||
* relocation entry in the the low 16 bits of r_address field.
|
||||
*/
|
||||
ARM_RELOC_HALF,
|
||||
ARM_RELOC_HALF_SECTDIFF
|
||||
};
|
||||
|
||||
|
||||
#endif /* __MACHO_HEADER_H */
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2013 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 <lib/image.h>
|
||||
#include <lib/macho.h>
|
||||
#include <lib/env.h>
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#if WITH_HW_MIU
|
||||
#include <platform/soc/miu.h>
|
||||
#endif
|
||||
#include <sys.h>
|
||||
#include <sys/menu.h>
|
||||
|
||||
#include "boot.h"
|
||||
|
||||
addr_t gMonitorEntry;
|
||||
addr_t gMonitorArgs;
|
||||
|
||||
/*
|
||||
* Only applicable if the platform memory map has allocations for
|
||||
* a monitor.
|
||||
*/
|
||||
#if WITH_MONITOR
|
||||
|
||||
static int loaded_monitor(addr_t secure_addr, size_t secure_size);
|
||||
|
||||
/*
|
||||
* This expects a known macho image of size 'size' is placed at address 'addr'.
|
||||
* This is helper function to single monitor + kernel load image, where monitor macho
|
||||
* is placed at the end of compressed kernelcache, and entire object is wrapped into
|
||||
* img3/4 format.
|
||||
*/
|
||||
int
|
||||
load_monitor_image(addr_t addr, size_t size)
|
||||
{
|
||||
if (size > platform_get_memory_region_size(kMemoryRegion_Monitor)) {
|
||||
printf("monitor too large\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
return (loaded_monitor(addr, size));
|
||||
}
|
||||
|
||||
static int
|
||||
loaded_monitor(addr_t secure_addr, size_t secure_size)
|
||||
{
|
||||
monitor_boot_args *mba;
|
||||
addr_t virtualBase;
|
||||
addr_t virtualEnd;
|
||||
addr_t monitor_base;
|
||||
addr_t monitor_size;
|
||||
|
||||
if (!macho_valid(secure_addr))
|
||||
return -6;
|
||||
|
||||
monitor_base = platform_get_memory_region_base(kMemoryRegion_Monitor);
|
||||
monitor_size = platform_get_memory_region_size(kMemoryRegion_Monitor);
|
||||
|
||||
if (!macho_load(secure_addr, secure_size, monitor_base, &virtualBase, &virtualEnd, &gMonitorEntry, 0))
|
||||
return -7;
|
||||
|
||||
dprintf(DEBUG_INFO, "monitor loaded at virt: %p, phys: %p, entry: %p\n",
|
||||
(void *)virtualBase, (void *)monitor_base, (void *)gMonitorEntry);
|
||||
|
||||
gMonitorEntry -= virtualBase;
|
||||
gMonitorEntry += monitor_base;
|
||||
|
||||
mba = (monitor_boot_args *)(monitor_base + monitor_size - sizeof(*mba));
|
||||
mba->version = 2;
|
||||
mba->virtBase = virtualBase;
|
||||
mba->physBase = monitor_base;
|
||||
mba->memSize = monitor_size;
|
||||
gMonitorArgs = (addr_t)mba;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WITH_MONITOR */
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Computer, 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 Computer, Inc.
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
OPTIONS += WITH_MACHO=1
|
||||
MODULES += \
|
||||
lib/cksum \
|
||||
lib/devicetree \
|
||||
lib/lzss
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/dt.o \
|
||||
$(LOCAL_DIR)/kernelcache.o \
|
||||
$(LOCAL_DIR)/macho.o \
|
||||
$(LOCAL_DIR)/monitor.o
|
||||
|
||||
Reference in new issue
Block a user