first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,908 @@
|
||||
/*
|
||||
* Copyright (C) 2009, 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 <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "DeviceTreePatcher.h"
|
||||
#include "Kernelcache.h"
|
||||
#include "LoadMachO.h"
|
||||
|
||||
#include <non_posix_types.h>
|
||||
#include <lib/image.h>
|
||||
|
||||
#define DEFAULT_SDRAM_BASE 0x00000000
|
||||
#define DEFAULT_SDRAM_LEN (256 * 1024 * 1024)
|
||||
#define TARGET_MMU_BASE_ALIGN 16384
|
||||
|
||||
#define ARM_B_POS_0x40 0xea00000e // b pc+0x40
|
||||
#define ARM_LDR_R0_PC 0xe59f0000 // ldr r0, [pc] (here+8)
|
||||
#define ARM_LDR_PC_PC 0xe59ff000 // ldr pc, [pc] (here+8)
|
||||
|
||||
#define ARM64_BOARD_SECURITY_EPOCH 0
|
||||
|
||||
// An arbitrarily sized framebuffer.
|
||||
#define FRAMEBUFFER_WIDTH 640
|
||||
#define FRAMEBUFFER_HEIGHT 960
|
||||
#define FRAMEBUFFER_DEPTH_BITS 32
|
||||
#define FRAMEBUFFER_STRIDE_BYTES \
|
||||
(FRAMEBUFFER_WIDTH * FRAMEBUFFER_DEPTH_BITS / 8)
|
||||
#define FRAMEBUFFER_SIZE_BYTES \
|
||||
(FRAMEBUFFER_STRIDE_BYTES * FRAMEBUFFER_HEIGHT * 3)
|
||||
|
||||
#define PANIC_SIZE 16384
|
||||
|
||||
#define PACKED __attribute__((packed))
|
||||
|
||||
#define USE_L4
|
||||
|
||||
#ifdef USE_L4
|
||||
#define MONITOR_SIZE (0x00800000)
|
||||
#endif
|
||||
|
||||
/* Just to make sure we get the alignment right */
|
||||
typedef uint64_t arm64_ptr_t __attribute__((aligned(8)));
|
||||
typedef uint64_t arm64_uint64_t __attribute__((aligned(8)));
|
||||
|
||||
// lib/macho/boot.h
|
||||
// This isn't taken verbatim: changed host-varying types to exact width,
|
||||
// as they would otherwise not work on 64 bit hosts.
|
||||
|
||||
/*
|
||||
* Video information..
|
||||
*/
|
||||
|
||||
#define BOOT_LINE_LENGTH 256
|
||||
|
||||
struct Boot_Video {
|
||||
uint32_t v_baseAddr; /* Base address of video memory */
|
||||
uint32_t v_display; /* Display Code (if Applicable */
|
||||
uint32_t v_rowBytes; /* Number of bytes per pixel row */
|
||||
uint32_t v_width; /* Width */
|
||||
uint32_t v_height; /* Height */
|
||||
uint32_t v_depth; /* Pixel Depth */
|
||||
} PACKED;
|
||||
|
||||
struct Boot_Video_64 {
|
||||
arm64_ptr_t v_baseAddr;/* Base address of video memory */
|
||||
arm64_ptr_t v_display; /* Display Code (if Applicable */
|
||||
arm64_uint64_t v_rowBytes; /* Number of bytes per pixel row */
|
||||
arm64_uint64_t v_width; /* Width */
|
||||
arm64_uint64_t v_height; /* Height */
|
||||
arm64_uint64_t v_depth; /* Pixel Depth */
|
||||
} PACKED;
|
||||
|
||||
/* Boot argument structure - passed into Mach kernel at boot time.
|
||||
*/
|
||||
#define kBootArgsRevision 1
|
||||
#define kBootArgsVersion1 1
|
||||
|
||||
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 */
|
||||
uint32_t deviceTreeAddr; /* Base of flattened device tree */
|
||||
uint32_t deviceTreeLength; /* Length of flattened tree */
|
||||
char commandLine[BOOT_LINE_LENGTH]; /* Passed in command line */
|
||||
} PACKED;
|
||||
|
||||
typedef struct boot_args_64 {
|
||||
uint16_t revision; /* Revision of boot_args structure */
|
||||
uint16_t version; /* Version of boot_args structure */
|
||||
arm64_ptr_t virtBase; /* Virtual base of memory */
|
||||
arm64_ptr_t physBase; /* Physical base of memory */
|
||||
arm64_uint64_t memSize; /* Size of memory */
|
||||
arm64_ptr_t topOfKernelData; /* Highest physical address used in kernel data area */
|
||||
Boot_Video_64 video; /* Video Information */
|
||||
uint32_t machineType; /* Machine Type */
|
||||
arm64_ptr_t deviceTreeAddr; /* Base of flattened device tree */
|
||||
uint32_t deviceTreeLength; /* Length of flattened tree */
|
||||
char commandLine[BOOT_LINE_LENGTH]; /* Passed in command line */
|
||||
} boot_args_64;
|
||||
|
||||
#ifdef USE_L4
|
||||
typedef struct monitor_boot_args {
|
||||
uint64_t version; /* structure version - this is version 1 */
|
||||
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 */
|
||||
} monitor_boot_args;
|
||||
#endif
|
||||
|
||||
struct dt_patch_s {
|
||||
const char *node;
|
||||
const char *property;
|
||||
const char *value;
|
||||
bool str;
|
||||
};
|
||||
typedef struct dt_patch_s dt_patch_t;
|
||||
|
||||
struct Options {
|
||||
const char *kernelcache_file;
|
||||
const char *devicetree_file;
|
||||
const char *ramdisk_file;
|
||||
const char *output_file;
|
||||
std::string bootargs_string;
|
||||
uint64_t sdram_base;
|
||||
uint32_t sdram_len;
|
||||
uint64_t virtual_base;
|
||||
bool override_virtual_base;
|
||||
bool have_boot_partition_id;
|
||||
unsigned boot_partition_id;
|
||||
std::vector<dt_patch_t> dt_patches;
|
||||
bool page_size_16kb;
|
||||
#ifdef USE_L4
|
||||
const char *l4_path;
|
||||
#endif
|
||||
|
||||
Options()
|
||||
: kernelcache_file(NULL),
|
||||
devicetree_file(NULL),
|
||||
ramdisk_file(NULL),
|
||||
output_file(NULL),
|
||||
bootargs_string(),
|
||||
sdram_base(DEFAULT_SDRAM_BASE),
|
||||
sdram_len(DEFAULT_SDRAM_LEN),
|
||||
virtual_base(0),
|
||||
override_virtual_base(false),
|
||||
have_boot_partition_id(false),
|
||||
boot_partition_id(),
|
||||
dt_patches(),
|
||||
page_size_16kb(false),
|
||||
#ifdef USE_L4
|
||||
l4_path(NULL)
|
||||
#endif
|
||||
{ }
|
||||
|
||||
bool Parse(int argc, char **argv);
|
||||
};
|
||||
|
||||
static void Usage(const char *argv0);
|
||||
static uint64_t RoundToNextPage(const Options &options, uint64_t address);
|
||||
static uint64_t RoundToNextMmuBase(uint64_t address);
|
||||
static bool SetFrequenciesInDeviceTree(DeviceTreePatcher *device_tree_patcher);
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Options options;
|
||||
if (!options.Parse(argc, argv)) {
|
||||
Usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *output_str;
|
||||
if (!strcmp(options.output_file, "-")) {
|
||||
// Output to stdout.
|
||||
output_str = stdout;
|
||||
} else {
|
||||
// Output to a file.
|
||||
output_str = fopen(options.output_file, "wb");
|
||||
if (output_str == NULL) {
|
||||
fprintf(stderr, "Couldn't open output file \"%s\"\n",
|
||||
options.output_file);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Load the kernelcache from an img3 file.
|
||||
Buffer kernelcache;
|
||||
if (!kernelcache.loadFromAuto(options.kernelcache_file,
|
||||
IMAGE_TYPE_KERNELCACHE)) {
|
||||
fprintf(stderr, "Couldn't load kernel cache %s\n",
|
||||
options.kernelcache_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Decompress the kernel cache to a Mach-O binary.
|
||||
Buffer macho;
|
||||
if (!DecompressKernelcache(kernelcache, &macho)) {
|
||||
fprintf(stderr, "Couldn't decompress kernel cache\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Unpack the Mach-O kernel into the base of 'ram'.
|
||||
Buffer ram;
|
||||
ram.alloc(options.sdram_len);
|
||||
LoadMachO load_macho(macho, &ram);
|
||||
if (options.override_virtual_base) {
|
||||
load_macho.OverrideVirtualBase(options.virtual_base);
|
||||
}
|
||||
if (!load_macho.Load()) {
|
||||
fprintf(stderr, "Couldn't unpack Mach-O kernel image\n");
|
||||
return 1;
|
||||
}
|
||||
uint64_t virtual_base;
|
||||
if (!load_macho.GetVirtualBase(&virtual_base)) {
|
||||
fprintf(stderr, "Couldn't infer virtual base of Mach-O kernel image\n");
|
||||
return false;
|
||||
}
|
||||
uint64_t phys_base = options.sdram_base;
|
||||
uint64_t image_size = load_macho.LastAddress();
|
||||
uint64_t entry_point_phys_offset = load_macho.EntryPointPhysOffset();
|
||||
|
||||
// Load the device tree from an img3 file.
|
||||
Buffer devicetree;
|
||||
if (!devicetree.loadFromAuto(options.devicetree_file, IMAGE_TYPE_DEVTREE)) {
|
||||
fprintf(stderr, "Couldn't load devicetree %s\n", options.devicetree_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load the ramdisk from an img3 file.
|
||||
Buffer ramdisk;
|
||||
if (options.ramdisk_file != NULL &&
|
||||
!ramdisk.loadFromAuto(options.ramdisk_file, IMAGE_TYPE_RAMDISK)) {
|
||||
fprintf(stderr, "Couldn't load ramdisk %s\n", options.ramdisk_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"kernelcache: %u\n"
|
||||
"macho: %u\n"
|
||||
"unpacked: %llu\n"
|
||||
"devicetree: %u\n"
|
||||
"ramdisk: %u\n",
|
||||
(unsigned) kernelcache.size(),
|
||||
(unsigned) macho.size(),
|
||||
image_size,
|
||||
(unsigned) devicetree.size(),
|
||||
(unsigned) ramdisk.size());
|
||||
|
||||
// Append the device tree to the unpacked Mach-O kernel.
|
||||
uint64_t devicetree_offset = RoundToNextPage(options, image_size);
|
||||
if (devicetree_offset + devicetree.size() > ram.size()) {
|
||||
fprintf(stderr, "Kernel + devicetree too large\n");
|
||||
return 1;
|
||||
}
|
||||
memcpy((char *) ram.buf() + devicetree_offset,
|
||||
devicetree.buf(),
|
||||
devicetree.size());
|
||||
|
||||
// Append the ramdisk to the device tree.
|
||||
uint64_t ramdisk_offset =
|
||||
RoundToNextPage(options, devicetree_offset + devicetree.size());
|
||||
memcpy((char *) ram.buf() + ramdisk_offset, ramdisk.buf(), ramdisk.size());
|
||||
|
||||
// Append bootargs to the device tree.
|
||||
uint64_t bootargs_offset =
|
||||
RoundToNextPage(options, ramdisk_offset + ramdisk.size());
|
||||
|
||||
// Framebuffer at the end of memory, followed by panic ram.
|
||||
uint64_t framebuffer_offset =
|
||||
options.sdram_len - PANIC_SIZE - FRAMEBUFFER_SIZE_BYTES;
|
||||
// Round down to a 1MB segment. MMU table initialization requires this.
|
||||
framebuffer_offset &= ~((1 << 20) - 1);
|
||||
|
||||
uint64_t end_of_image = 0;
|
||||
uint64_t end_of_image_offset = 0;
|
||||
|
||||
#ifdef USE_L4
|
||||
monitor_boot_args mba;
|
||||
uint64_t mba_offset;
|
||||
|
||||
if( options.l4_path )
|
||||
{
|
||||
uint64_t monitor_offset = (framebuffer_offset - MONITOR_SIZE) & ~(0x200000ULL - 1); // 2MB-aligned
|
||||
mba_offset = monitor_offset + MONITOR_SIZE - sizeof(mba);
|
||||
|
||||
mba.version = 1;
|
||||
mba.virtBase = 0; // L4 doesn't care
|
||||
mba.physBase = monitor_offset + phys_base; // L4 doesn't care
|
||||
mba.memSize = MONITOR_SIZE;
|
||||
mba.kernArgs = bootargs_offset + phys_base;
|
||||
mba.kernEntry = entry_point_phys_offset + phys_base;
|
||||
fprintf(stderr, "monitor at %llx\n", monitor_offset);
|
||||
|
||||
end_of_image_offset =
|
||||
RoundToNextMmuBase(monitor_offset + MONITOR_SIZE);
|
||||
|
||||
Buffer l4;
|
||||
if (!l4.loadFromAuto(options.l4_path,
|
||||
IMAGE_TYPE_MONITOR)) {
|
||||
fprintf(stderr, "Couldn't open monitor image\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Opened monitor image\n");
|
||||
LoadMachO load_l4(l4, &ram);
|
||||
fprintf(stderr, "loaded monitor image\n");
|
||||
|
||||
// L4 in 16KB mode is just a mini monitor with a different
|
||||
// link address.
|
||||
uint64_t l4_link_base =
|
||||
options.page_size_16kb
|
||||
? 0x0000004000000000ULL
|
||||
: 0x0000004100000000ULL;
|
||||
load_l4.OverrideVirtualBase(l4_link_base - monitor_offset);
|
||||
if (options.override_virtual_base) {
|
||||
fprintf(stderr, "overriding virt base\n");
|
||||
load_l4.OverrideVirtualBase(options.virtual_base);
|
||||
fprintf(stderr, "overrode virt base\n");
|
||||
}
|
||||
fprintf(stderr, "about to load\n");
|
||||
if (!load_l4.Load()) {
|
||||
fprintf(stderr, "Couldn't unpack Mach-O kernel image\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* re-set the entry point now to use L4's */
|
||||
entry_point_phys_offset = load_l4.EntryPointPhysOffset();
|
||||
fprintf(stderr, "Loaded l4: ept %llx\n", entry_point_phys_offset);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
end_of_image_offset =
|
||||
RoundToNextMmuBase(bootargs_offset + sizeof(boot_args));
|
||||
|
||||
end_of_image = phys_base + end_of_image_offset;
|
||||
if (end_of_image_offset > framebuffer_offset) {
|
||||
fprintf(stderr, "Image size runs into framebuffer ram (%llx vs %llx)\n",
|
||||
end_of_image_offset, framebuffer_offset);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Place a bootstrap to load r0 with the offset of the boot args,
|
||||
// and jump to the entry point.
|
||||
if (load_macho.Is64Bit()) {
|
||||
// Don't need to use exception vector to get started on ARM64
|
||||
|
||||
// [~] $ cat bootstrap.s
|
||||
// adr x0, Lboot_args_addr
|
||||
// adr x1, Lentry_addr
|
||||
// ldr x0, [x0] /* Load boot args address */
|
||||
// ldr x1, [x1] /* Load entry point */
|
||||
// br x1 /* Jump to entry point */
|
||||
// .long 0 /* Padding */
|
||||
// Lboot_args_addr:
|
||||
// .quad 0xfeedbeef
|
||||
// Lentry_addr:
|
||||
// .quad 0xabcdef11
|
||||
//
|
||||
// [~] $ xcrun -sdk iphoneos clang -arch arm64 -c bootstrap.s
|
||||
// [~] $ otool -t bootstrap.o
|
||||
// bootstrap.o:
|
||||
// (__TEXT,__text) section
|
||||
// 00000000 100000c0 100000e1 f9400000 f9400021
|
||||
// 00000010 d61f0020 00000000 feedbeef 00000000
|
||||
// 00000020 abcdef11 00000000
|
||||
|
||||
// Validated by hand that "adr" instructions are encoded as desired.
|
||||
uint64_t entry_point_phys = phys_base + entry_point_phys_offset;
|
||||
uint64_t boot_args_phys = phys_base + bootargs_offset;
|
||||
|
||||
uint32_t bootstrap[] = {
|
||||
0xd5034fdf, // msr DAIFSet, #15
|
||||
0xd5033fdf, // isb sy
|
||||
0x100000c0, // Get location of boot args address: adr x0, Lboot_args_addr (pc + 24)
|
||||
0x100000e1, // Get location of entry point: adr x1, Lentry_addr (pc + 32)
|
||||
0xf9400000, // Load address of boot args: ldr x0, [x0]
|
||||
0xf9400021, // Load entry point: ldr x1, [x1]
|
||||
0xd61f0020, // Jump to entry point
|
||||
0, // Padding for proper alignment (we reset with all memory as "device")
|
||||
(uint32_t) boot_args_phys, // Boot args address (low 32 bit)
|
||||
(uint32_t) (boot_args_phys >> 32), // Boot args address (high 32 bits)
|
||||
(uint32_t) entry_point_phys, // Entry address (low 32 bits)
|
||||
(uint32_t) (entry_point_phys >> 32), // Entry address (high 32 bits)
|
||||
0
|
||||
};
|
||||
|
||||
#ifdef USE_L4
|
||||
if( options.l4_path ) {
|
||||
memcpy((char *)ram.buf() + (uintptr_t)mba_offset, &mba, sizeof(mba));
|
||||
uint64_t mba_phys = phys_base + mba_offset;
|
||||
bootstrap[8] = (uint32_t) mba_phys; /* Pointer to monitor_boot_args */
|
||||
bootstrap[9] = (uint32_t) (mba_phys >> 32);
|
||||
}
|
||||
#endif
|
||||
memcpy(ram.buf(), bootstrap, sizeof(bootstrap));
|
||||
|
||||
} else {
|
||||
uint32_t bootstrap[] = {
|
||||
ARM_B_POS_0x40, // 0x00: b 0x40
|
||||
0, // undef exception
|
||||
0, // swi exception
|
||||
0, // pabt exception
|
||||
0, // dabt exception
|
||||
0, // reserved exception
|
||||
0, // irq exception
|
||||
0, // fiq exception
|
||||
0, // 0x20: exception vector table
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
ARM_LDR_R0_PC, // 0x40: ldr r0, [pc] ; ldr r0, [0x48]
|
||||
ARM_LDR_PC_PC, // 0x44: ldr pc, [pc] ; ldr pc, [0x4c]
|
||||
(uint32_t) phys_base + (uint32_t) bootargs_offset,// 0x48: .word bootargs_offset
|
||||
(uint32_t) (phys_base + entry_point_phys_offset) // 0x4c: .word entry_point
|
||||
};
|
||||
|
||||
assert(phys_base == (uint32_t)phys_base);
|
||||
assert(bootargs_offset == (uint32_t)bootargs_offset);
|
||||
assert((phys_base + bootargs_offset) == (uint32_t)(phys_base + bootargs_offset));
|
||||
|
||||
memcpy(ram.buf(), bootstrap, sizeof(bootstrap));
|
||||
}
|
||||
|
||||
// Place the boot args into memory now that we have the location of
|
||||
// everything.
|
||||
if (load_macho.Is64Bit()) {
|
||||
printf("doing 64-bit boot-args.\n");
|
||||
boot_args_64 bootArgs;
|
||||
|
||||
memset(&bootArgs, 0, sizeof(bootArgs));
|
||||
bootArgs.revision = kBootArgsRevision;
|
||||
bootArgs.version = kBootArgsVersion1 + ARM64_BOARD_SECURITY_EPOCH;
|
||||
bootArgs.virtBase = virtual_base;
|
||||
bootArgs.physBase = phys_base;
|
||||
bootArgs.memSize = framebuffer_offset; // Must be 1MB aligned.
|
||||
// Physical address of "video" ram.
|
||||
bootArgs.video.v_baseAddr = phys_base + framebuffer_offset;
|
||||
bootArgs.video.v_display = 1; // Display code is just 1
|
||||
bootArgs.video.v_rowBytes = FRAMEBUFFER_STRIDE_BYTES;
|
||||
bootArgs.video.v_width = FRAMEBUFFER_WIDTH;
|
||||
bootArgs.video.v_height = FRAMEBUFFER_HEIGHT;
|
||||
bootArgs.video.v_depth = FRAMEBUFFER_DEPTH_BITS;
|
||||
// Physical address. MMU L1 tables are built here.
|
||||
#ifdef USE_L4
|
||||
if( options.l4_path )
|
||||
bootArgs.topOfKernelData = RoundToNextMmuBase(phys_base + bootargs_offset + sizeof(boot_args));
|
||||
else
|
||||
#endif
|
||||
bootArgs.topOfKernelData = end_of_image;
|
||||
// Virtual address of device tree.
|
||||
bootArgs.deviceTreeAddr = virtual_base + devicetree_offset;
|
||||
bootArgs.deviceTreeLength = devicetree.size();
|
||||
memcpy(bootArgs.commandLine,
|
||||
options.bootargs_string.data(),
|
||||
options.bootargs_string.size());
|
||||
memcpy((char *) ram.buf() + bootargs_offset, &bootArgs, sizeof(bootArgs));
|
||||
} else {
|
||||
boot_args bootArgs;
|
||||
|
||||
memset(&bootArgs, 0, sizeof(bootArgs));
|
||||
bootArgs.revision = kBootArgsRevision;
|
||||
bootArgs.version = kBootArgsVersion1;
|
||||
bootArgs.virtBase = (uint32_t)virtual_base;
|
||||
bootArgs.physBase = (uint32_t)phys_base;
|
||||
bootArgs.memSize = (uint32_t)framebuffer_offset; // Must be 1MB aligned.
|
||||
// Physical address of "video" ram.
|
||||
bootArgs.video.v_baseAddr = (uint32_t)(phys_base + framebuffer_offset);
|
||||
bootArgs.video.v_display = 1; // Display code is just 1
|
||||
bootArgs.video.v_rowBytes = FRAMEBUFFER_STRIDE_BYTES;
|
||||
bootArgs.video.v_width = FRAMEBUFFER_WIDTH;
|
||||
bootArgs.video.v_height = FRAMEBUFFER_HEIGHT;
|
||||
bootArgs.video.v_depth = FRAMEBUFFER_DEPTH_BITS;
|
||||
// Physical address. MMU L1 tables are built here.
|
||||
bootArgs.topOfKernelData = (uint32_t)end_of_image;
|
||||
// Virtual address of device tree.
|
||||
bootArgs.deviceTreeAddr = (uint32_t)(virtual_base + devicetree_offset);
|
||||
bootArgs.deviceTreeLength = devicetree.size();
|
||||
memcpy(bootArgs.commandLine,
|
||||
options.bootargs_string.data(),
|
||||
options.bootargs_string.size());
|
||||
memcpy((char *) ram.buf() + bootargs_offset, &bootArgs, sizeof(bootArgs));
|
||||
}
|
||||
|
||||
// Patch the device tree with the location of loaded segments.
|
||||
DeviceTreePatcher device_tree_patcher((char *) ram.buf() + devicetree_offset, devicetree.size());
|
||||
|
||||
// There's a large number of properties not being set here, such as
|
||||
// 'firmware-version', 'pram', 'vram', but these are not necessary
|
||||
// yet.
|
||||
|
||||
// Patch the location of the device tree itself.
|
||||
if (!device_tree_patcher.AllocateMemoryRange("DeviceTree",
|
||||
phys_base + devicetree_offset,
|
||||
devicetree.size())) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Patch the location of the ramdisk, if used.
|
||||
if (options.ramdisk_file != NULL &&
|
||||
!device_tree_patcher.AllocateMemoryRange("RAMDisk",
|
||||
phys_base + ramdisk_offset,
|
||||
ramdisk.size())) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Patch the location of the bootargs.
|
||||
if (!device_tree_patcher.AllocateMemoryRange("BootArgs",
|
||||
phys_base + bootargs_offset,
|
||||
sizeof(boot_args))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Patch KernelCache segments into the device tree.
|
||||
for (LoadMachO::SegmentListIterator it = load_macho.SegmentListBegin();
|
||||
it != load_macho.SegmentListEnd();
|
||||
++it) {
|
||||
if (!device_tree_patcher.AllocateMemoryRange(it->name.c_str(),
|
||||
it->pmaddr,
|
||||
it->size)) {
|
||||
return 1;
|
||||
}
|
||||
// Also check that no segment overlaps the first 4KB page of
|
||||
// memory. That would mean the bootstrap overwrote something.
|
||||
if (it->pmaddr < 4096 && it->size != 0) {
|
||||
fprintf(stderr, "KernelCache segment \"%s\" overlaps first 4KB\n",
|
||||
it->name.c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set /chosen:debug-enabled=1.
|
||||
if (!device_tree_patcher.SetPropertyInt("chosen", "debug-enabled", 1)) {
|
||||
fprintf(stderr, "Couldn't set /chosen:debug-enabled=1\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Set (arbitrary) core and peripheral frequencies in the device tree.
|
||||
if (!SetFrequenciesInDeviceTree(&device_tree_patcher)) {
|
||||
fprintf(stderr, "Couldn't set device frequencies\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (options.have_boot_partition_id) {
|
||||
// Set /chosen/root-matching=<xml>...boot_partition_id...</xml>.
|
||||
char s[256];
|
||||
snprintf(s, sizeof(s),
|
||||
"<dict>"
|
||||
"<key>IOProviderClass</key>"
|
||||
"<string>IOMedia</string>"
|
||||
"<key>IOPropertyMatch</key>"
|
||||
"<dict><key>Partition ID</key><integer>%u</integer></dict>"
|
||||
"</dict>",
|
||||
options.boot_partition_id);
|
||||
if (!device_tree_patcher.SetPropertyString("chosen", "root-matching", s)) {
|
||||
fprintf(stderr, "Couldn't set /chosen:root-matching=%s\n", s);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the location of panic ram. First word physical base, second word size.
|
||||
uint64_t panic_prop[2];
|
||||
bool wide_panic_prop;
|
||||
|
||||
uint64_t vram_prop[2];
|
||||
|
||||
switch (device_tree_patcher.GetNumAddressCells("")) { // Empty string -> root
|
||||
case 1: {
|
||||
wide_panic_prop = false;
|
||||
panic_prop[0] = ((uint64_t)PANIC_SIZE << 32) | (phys_base + options.sdram_len - PANIC_SIZE);
|
||||
if (!device_tree_patcher.SetPropertyInt("pram", "reg", panic_prop[0])) {
|
||||
fprintf(stderr, "Couldn't set /pram:reg=0x%016llx\n", panic_prop[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vram_prop[0] = (((uint64_t)FRAMEBUFFER_SIZE_BYTES) << 32) | (phys_base + framebuffer_offset);
|
||||
if (!device_tree_patcher.SetPropertyInt("vram", "reg", vram_prop[0])) {
|
||||
fprintf(stderr, "Couldn't set /vram:reg=0x%016llx\n", vram_prop[0]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
wide_panic_prop = true;
|
||||
panic_prop[0] = phys_base + options.sdram_len - PANIC_SIZE;
|
||||
panic_prop[1] = ((uint64_t) PANIC_SIZE);
|
||||
if (!device_tree_patcher.SetPropertyTwoQuads("pram", "reg", panic_prop)) {
|
||||
fprintf(stderr, "Couldn't set /pram:reg=0x%016llx,0x%016llx\n", panic_prop[0], panic_prop[1]);
|
||||
return 1;
|
||||
}
|
||||
vram_prop[0] = phys_base + framebuffer_offset;
|
||||
vram_prop[1] = FRAMEBUFFER_SIZE_BYTES;
|
||||
if (!device_tree_patcher.SetPropertyTwoQuads("vram", "reg", vram_prop)) {
|
||||
fprintf(stderr, "Couldn't set /vram:reg=0x%016llx,0x%016llx\n", vram_prop[0], vram_prop[1]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
fprintf(stderr, "Can't set pram address because number of address cells is bad.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<dt_patch_t>::const_iterator patch_iterator;
|
||||
for (patch_iterator = options.dt_patches.begin(); patch_iterator != options.dt_patches.end(); ++patch_iterator) {
|
||||
dt_patch_t patch = *patch_iterator;
|
||||
if (patch.str) {
|
||||
fprintf(stderr, "Patching /%s:%s=%s\n", patch.node, patch.property, patch.value);
|
||||
if (!device_tree_patcher.SetPropertyString(patch.node, patch.property, patch.value)) {
|
||||
fprintf(stderr, "Couldn't set /%s:%s=%s\n", patch.node, patch.property, patch.value);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
uint64_t value = atoi(patch.value);
|
||||
fprintf(stderr, "Patching /%s:%s=0x%016llx\n", patch.node, patch.property, value);
|
||||
if (!device_tree_patcher.SetPropertyInt(patch.node, patch.property, value)) {
|
||||
fprintf(stderr, "Couldn't set /%s:%s=0x%016llx\n", patch.node, patch.property, value);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(end_of_image_offset == (size_t)end_of_image_offset);
|
||||
fwrite(ram.buf(), (size_t)end_of_image_offset, 1, output_str);
|
||||
|
||||
fprintf(stderr,
|
||||
"KernelCache: %s\n"
|
||||
"DeviceTree: %s\n"
|
||||
"SDRAM size: %uMB\n"
|
||||
"SDRAM base: 0x%llx\n"
|
||||
"Virtual base: 0x%016llx\n"
|
||||
"Top of k. data: 0x%016llx\n"
|
||||
"Output image: %s\n"
|
||||
"Boot args: %s\n"
|
||||
"Ramdisk: %s at 0x%llx, %u\n"
|
||||
"Framebuffer: 0x%llx %dx%dx%dbpp (%uKB)\n"
|
||||
"Panic ram: 0x%llx size %lluKB\n"
|
||||
"Image size: %llu bytes\n"
|
||||
"Entry point: 0x%016llx\n"
|
||||
"Boot partition: %s\n",
|
||||
options.kernelcache_file,
|
||||
options.devicetree_file,
|
||||
options.sdram_len / 1024 / 1024,
|
||||
options.sdram_base,
|
||||
virtual_base,
|
||||
end_of_image,
|
||||
strcmp(options.output_file, "-") ? options.output_file : "<stdout>",
|
||||
options.bootargs_string.c_str(),
|
||||
options.ramdisk_file ? options.ramdisk_file : "(none)",
|
||||
phys_base + ramdisk_offset,
|
||||
(unsigned) ramdisk.size(),
|
||||
phys_base + framebuffer_offset,
|
||||
FRAMEBUFFER_WIDTH,
|
||||
FRAMEBUFFER_HEIGHT,
|
||||
FRAMEBUFFER_DEPTH_BITS,
|
||||
(unsigned) RoundToNextPage(options, FRAMEBUFFER_SIZE_BYTES) / 1024,
|
||||
wide_panic_prop ? panic_prop[0] : panic_prop[0] & 0xFFFFFFFF,
|
||||
wide_panic_prop ? panic_prop[1] / 1024 : (panic_prop[0] & 0xFFFFFFFF00000000ULL) >> 44,
|
||||
end_of_image - phys_base,
|
||||
phys_base + entry_point_phys_offset,
|
||||
options.have_boot_partition_id ? "Root filesystem" : "Ramdisk");
|
||||
|
||||
if (output_str != stdout) {
|
||||
fclose(output_str);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Usage(const char *argv0) {
|
||||
fprintf(stderr,
|
||||
"Usage:\n"
|
||||
" %s <flags>\n"
|
||||
"\n"
|
||||
"Flags are: ('*' denotes mandatory)\n"
|
||||
"\n"
|
||||
"* -k <kernelcache>\n"
|
||||
" Specify the kernelcache file to use. This is decompressed\n"
|
||||
" and the contained Mach-O binary unpacked at the start of\n"
|
||||
" the resulting image\n"
|
||||
"* -d <devicetree>\n"
|
||||
" Specify the devicetree file to use. This is extracted and\n"
|
||||
" appended to the kernelcache in the resulting image.\n"
|
||||
" -r <ramdisk>\n"
|
||||
" Specify the ramdisk file to use. This is extracted and\n"
|
||||
" appended to the devicetree in the resulting image. Optional.\n"
|
||||
"* -o <outputfile>\n"
|
||||
" Specify the output file to use. This will contain the image\n"
|
||||
" to be loaded into memory starting at the base of SDRAM.\n"
|
||||
" You can use '-' to pipe to standard output.\n"
|
||||
" -m <ramsize>\n"
|
||||
" The target's memory size, in MB. This is inserted into the\n"
|
||||
" boot args structure, and bounds checked when creating the\n"
|
||||
" image. This does not change the image size.\n"
|
||||
" Default is %uMB.\n"
|
||||
" -v <virtualbase>\n"
|
||||
" The target's kernel virtual memory base. This should match\n"
|
||||
" the layout of he input KernelCache or it will fail to\n"
|
||||
" translate to the base of SDRAM.\n"
|
||||
" Default inferred from the layout of the kernelcache.\n"
|
||||
" -s <sdrambase>\n"
|
||||
" The target's SDRAM memory base. This is the location where\n"
|
||||
" the output image will be loaded on the target.\n"
|
||||
" Default is 0x%08x.\n"
|
||||
" -p <node> <property> <value>\n"
|
||||
" Patch the devicetree property with the specified integer value.\n"
|
||||
" -P <node> <property> <value>\n"
|
||||
" Patch the devicetree property with the specified string value.\n"
|
||||
" -K\n"
|
||||
" Tweak offsets for expected kernel layout with 16KB pages.\n"
|
||||
" -- <bootargs> [...]\n"
|
||||
" All remaining arguments after '--' are copied into the boot\n"
|
||||
" args structure as strings separated by spaces.\n"
|
||||
" Default is an empty string.\n"
|
||||
"",
|
||||
argv0,
|
||||
DEFAULT_SDRAM_LEN / 1024 / 1024,
|
||||
DEFAULT_SDRAM_BASE);
|
||||
}
|
||||
|
||||
bool Options::Parse(int argc, char **argv) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "-k")) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-k must be followed by the kernel cache file name\n");
|
||||
return false;
|
||||
}
|
||||
kernelcache_file = argv[++i];
|
||||
} else if (!strcmp(argv[i], "-d")) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-d must be followed by the devicetree file name\n");
|
||||
return false;
|
||||
}
|
||||
devicetree_file = argv[++i];
|
||||
} else if (!strcmp(argv[i], "-r")) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-r must be followed by the ramdisk file name\n");
|
||||
return false;
|
||||
}
|
||||
ramdisk_file = argv[++i];
|
||||
} else if (!strcmp(argv[i], "-m")) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-m must be followed by the SDRAM size in MB\n");
|
||||
return false;
|
||||
}
|
||||
sdram_len = strtoul(argv[++i], NULL, 0) * 1024 * 1024;
|
||||
} else if (!strcmp(argv[i], "-o")) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-o must be followed by an output file name\n");
|
||||
return false;
|
||||
}
|
||||
output_file = argv[++i];
|
||||
} else if (!strcmp(argv[i], "-s")) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-s must be followed by an address\n");
|
||||
return false;
|
||||
}
|
||||
sdram_base = strtoull(argv[++i], NULL, 0);
|
||||
} else if (!strcmp(argv[i], "-v")) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-v must be followed by an address\n");
|
||||
return false;
|
||||
}
|
||||
virtual_base = strtoull(argv[++i], NULL, 0);
|
||||
override_virtual_base = true;
|
||||
} else if (!strcmp(argv[i], "-b")) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-b must be followed by a partition id\n");
|
||||
return false;
|
||||
}
|
||||
have_boot_partition_id = true;
|
||||
boot_partition_id = strtoul(argv[++i], NULL, 0);
|
||||
} else if (!strcmp(argv[i], "-p")) {
|
||||
if (i + 3 >= argc) {
|
||||
fprintf(stderr, "-p must be followed by a node, property, and value\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
dt_patch_t patch = { argv[i+1], argv[i+2], argv[i+3], false };
|
||||
dt_patches.push_back(patch);
|
||||
i += 3;
|
||||
} else if (!strcmp(argv[i], "-P")) {
|
||||
if (i + 3 >= argc) {
|
||||
fprintf(stderr, "-P must be followed by a node, property, and value\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
dt_patch_t patch = { argv[i+1], argv[i+2], argv[i+3], true };
|
||||
dt_patches.push_back(patch);
|
||||
i += 3;
|
||||
} else if (!strcmp(argv[i], "-K")) {
|
||||
page_size_16kb = true;
|
||||
#ifdef USE_L4
|
||||
} else if (!strcmp(argv[i], "-l")) {
|
||||
fprintf(stderr, "got an l4!\n");
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "-l must be followed by the L4 file name\n");
|
||||
return false;
|
||||
}
|
||||
l4_path = argv[++i];
|
||||
#endif
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
// Consume the remaining arguments for bootargs.
|
||||
for (++i; i < argc; ++i) {
|
||||
if (!bootargs_string.empty()) bootargs_string += ' ';
|
||||
bootargs_string += argv[i];
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Unrecognized option \"%s\"\n", argv[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (kernelcache_file == NULL ||
|
||||
devicetree_file == NULL ||
|
||||
output_file == NULL) {
|
||||
fprintf(stderr, "Must specify kernelcache, devicetree and output file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bootargs_string.size() >= BOOT_LINE_LENGTH) {
|
||||
fprintf(stderr, "Boot args too long (%u >= %u)\n",
|
||||
(unsigned) bootargs_string.size(),
|
||||
(unsigned) BOOT_LINE_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _panic(const char *func, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, "\npanic in %s: ", func);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n\n");
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static uint64_t RoundToNextPage(const Options &options, uint64_t address) {
|
||||
// Round an address up to the next page boundary if not aligned (4KB or 16KB)
|
||||
uint64_t page_size = options.page_size_16kb ? 16384 : 4096;
|
||||
address += page_size - 1;
|
||||
address &= ~((uint64_t) page_size - 1);
|
||||
return address;
|
||||
}
|
||||
|
||||
static uint64_t RoundToNextMmuBase(uint64_t address) {
|
||||
// Round an address up to the next MMU base alignment (16KB).
|
||||
address += TARGET_MMU_BASE_ALIGN - 1;
|
||||
address &= ~((uint64_t) TARGET_MMU_BASE_ALIGN - 1);
|
||||
return address;
|
||||
}
|
||||
|
||||
static bool SetFrequenciesInDeviceTree(DeviceTreePatcher *device_tree_patcher) {
|
||||
struct {
|
||||
const char *name;
|
||||
uint32_t mhz;
|
||||
} cpu_clocks[] = {
|
||||
{ "clock-frequency", 1000 },
|
||||
{ "memory-frequency", 500 },
|
||||
{ "bus-frequency", 250 },
|
||||
{ "peripheral-frequency", 250 },
|
||||
{ "fixed-frequency", 24 },
|
||||
{ "timebase-frequency", 24 },
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(cpu_clocks) / sizeof(cpu_clocks[0]); ++i) {
|
||||
assert(cpu_clocks[i].mhz <= UINT32_MAX / 1000000);
|
||||
if (!device_tree_patcher->SetPropertyInt("cpus/cpu0",
|
||||
cpu_clocks[i].name,
|
||||
cpu_clocks[i].mhz * 1000000)) {
|
||||
fprintf(stderr, "Couldn't set cpus/cpu0:%s = %uMHz\n",
|
||||
cpu_clocks[i].name, (unsigned) cpu_clocks[i].mhz);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Buffer.h"
|
||||
extern "C" {
|
||||
#include "drivers/sha1.h"
|
||||
#include "lib/image/image3/Image3.h"
|
||||
#include "lib/image/image3/Image3Format.h"
|
||||
#if BOOTX2BIN_WITH_IMAGE4
|
||||
# include <libDER/asn1Types.h>
|
||||
# include <libDER/DER_Decode.h>
|
||||
# include <Img4Decode.h>
|
||||
#endif
|
||||
};
|
||||
|
||||
extern "C" int image3_load(struct image_info *image_info,
|
||||
u_int32_t type,
|
||||
void **load_addr, size_t *load_len);
|
||||
|
||||
void Buffer::alloc(size_t size) {
|
||||
free();
|
||||
_buf = new char[size];
|
||||
_size = size;
|
||||
memset(_buf, 0, _size);
|
||||
}
|
||||
|
||||
void Buffer::free() {
|
||||
delete[] _buf;
|
||||
_buf = NULL;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
void Buffer::swap(Buffer *other) {
|
||||
std::swap(_buf, other->_buf);
|
||||
std::swap(_size, other->_size);
|
||||
}
|
||||
|
||||
void Buffer::crop(size_t newSize) {
|
||||
assert(newSize <= _size);
|
||||
_size = newSize;
|
||||
}
|
||||
|
||||
bool Buffer::loadFromFile(const char *file) {
|
||||
// Load the contents of a file into a Buffer and return it.
|
||||
FILE *str = fopen(file, "rb");
|
||||
if (!str) {
|
||||
fprintf(stderr, "Couldn't open \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
fseek(str, 0, SEEK_END);
|
||||
size_t size = ftell(str);
|
||||
fseek(str, 0, SEEK_SET);
|
||||
Buffer file_buf;
|
||||
file_buf.alloc(size);
|
||||
int got = fread(file_buf.buf(), file_buf.size(), 1, str);
|
||||
fclose(str);
|
||||
if (got != 1) {
|
||||
fprintf(stderr, "Error reading \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
file_buf.swap(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if BOOTX2BIN_WITH_IMAGE4
|
||||
bool Buffer::loadFromIm4p(const char *file, uint32_t type, bool quiet) {
|
||||
// Load a payload of expected 'type' from 'file', which is in im4p
|
||||
// format. Returns a Buffer containing the payload.
|
||||
Buffer image_buf;
|
||||
if (!image_buf.loadFromFile(file)) {
|
||||
fprintf(stderr, "Couldn't load file \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decode IM4P DER format.
|
||||
DERItem im4p;
|
||||
im4p.data = (DERByte *) image_buf.buf();
|
||||
im4p.length = image_buf.size();
|
||||
DERReturn ret;
|
||||
DERImg4Payload decoded;
|
||||
memset(&decoded, 0, sizeof(decoded));
|
||||
ret = DERImg4DecodePayload(&im4p, &decoded);
|
||||
if (ret != DR_Success) {
|
||||
// This isn't a .im4p
|
||||
if (!quiet)
|
||||
fprintf(stderr, "Failed to decode .im4p header: %d\n", (int) ret);
|
||||
return false;
|
||||
}
|
||||
if (decoded.tag.length != 4 || decoded.type.length != 4) {
|
||||
fprintf(stderr, "Decoded .im4p header has bad tag lengths\n");
|
||||
return false;
|
||||
}
|
||||
if (memcmp(decoded.tag.data, "IM4P", 4) != 0) {
|
||||
fprintf(stderr, "DER file schema matches but not an IM4P?\n");
|
||||
return false;
|
||||
}
|
||||
uint32_t der_type = 0;
|
||||
ret = DERParseInteger(&decoded.type, &der_type);
|
||||
if (ret != DR_Success) {
|
||||
fprintf(stderr, "Failed to decode DER type field: %d\n", (int) ret);
|
||||
return false;
|
||||
}
|
||||
if (der_type != type) {
|
||||
fprintf(stderr, "DER type 0x%08x mismatches expected 0x%08x\n",
|
||||
der_type, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the payload.
|
||||
void *payload_addr = decoded.data.data;
|
||||
size_t payload_size = decoded.data.length;
|
||||
|
||||
uint8_t *buf_end = (uint8_t *) image_buf.buf() + image_buf.size();
|
||||
uint8_t *data_end = (uint8_t *) payload_addr + payload_size;
|
||||
if (data_end > buf_end) {
|
||||
fprintf(stderr, "DER data is truncated\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the data down to the base of the buffer.
|
||||
memmove(image_buf.buf(), payload_addr, payload_size);
|
||||
image_buf.crop(payload_size);
|
||||
image_buf.swap(this);
|
||||
return true;
|
||||
}
|
||||
#endif // BOOTX2BIN_WITH_IMAGE4
|
||||
|
||||
bool Buffer::loadFromImg3(const char *file, uint32_t type) {
|
||||
// Load a payload of expected 'type' from 'file', which is in img3
|
||||
// format. Returns a Buffer containing the payload.
|
||||
Buffer image_buf;
|
||||
if (!image_buf.loadFromFile(file)) {
|
||||
fprintf(stderr, "Couldn't load file \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make a handle for the buffer.
|
||||
Image3ObjectHandle handle;
|
||||
if (image3InstantiateFromBuffer(&handle,
|
||||
image_buf.buf(),
|
||||
image_buf.size(),
|
||||
false /* copy */)) {
|
||||
fprintf(stderr, "Image3 instantiation failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the payload.
|
||||
void *payload_addr = NULL;
|
||||
size_t payload_size = 0;
|
||||
if (image3GetTagStruct(handle,
|
||||
kImage3TagTypeData,
|
||||
&payload_addr,
|
||||
&payload_size,
|
||||
0)) {
|
||||
fprintf(stderr, "Couldn't find Image3 payload\n");
|
||||
image3Discard(&handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the data down to the base of the buffer.
|
||||
memmove(image_buf.buf(), payload_addr, payload_size);
|
||||
image_buf.crop(payload_size);
|
||||
image_buf.swap(this);
|
||||
image3Discard(&handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Buffer::loadFromAuto(const char *file, uint32_t type) {
|
||||
// Try raw - in any case we can check the type from there.
|
||||
if (!loadFromFile(file)) {
|
||||
fprintf(stderr, "Couldn't load file \"%s\"\n", file);
|
||||
return false;
|
||||
}
|
||||
if (memcmp(buf(), "3gmI", 4)) {
|
||||
#if BOOTX2BIN_WITH_IMAGE4
|
||||
// Try laoding as .im4p - suppress failure message as this is just a test.
|
||||
if (loadFromIm4p(file, type, true)) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
fprintf(stderr, "Treating as raw file: %s\n", file);
|
||||
return true;
|
||||
} else {
|
||||
fprintf(stderr, "Treating as img3 file: %s\n", file);
|
||||
return loadFromImg3(file, type);
|
||||
}
|
||||
}
|
||||
|
||||
// Externals used by Image3.c
|
||||
extern "C" int image3AESDecryptUsingLocalKey(void *buffer, size_t length) {
|
||||
fprintf(stderr, "Can't personalize\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
image3SHA1Generate(void *dataBuffer, size_t dataSize, void *hashBuffer) {
|
||||
sha1_calculate(dataBuffer, dataSize, hashBuffer);
|
||||
}
|
||||
|
||||
extern "C" int image3PKIVerifyHash(void *hashBuffer,
|
||||
size_t hashSize,
|
||||
void *signedHashBuffer,
|
||||
size_t signedHashSize,
|
||||
void *certBlobBuffer,
|
||||
size_t certBlobSize,
|
||||
void **certCustomData,
|
||||
size_t *certCustomDataSize) {
|
||||
fprintf(stderr, "Can't verify hash\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int image3TicketVerifyHash(void *hashBuffer,
|
||||
size_t hashSize,
|
||||
uint32_t imageType,
|
||||
uint32_t expectedType) {
|
||||
fprintf(stderr, "Can't verify hash\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" void *image3Malloc(size_t size) {
|
||||
return calloc(size, 1);
|
||||
}
|
||||
|
||||
extern "C" void image3Free(void *ptr, size_t size __unused) {
|
||||
free(ptr);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.
|
||||
*/
|
||||
|
||||
#ifndef BUFFER_H
|
||||
#define BUFFER_H 1
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
/*
|
||||
* Encapsulation of a memory buffer. The contained buffer is
|
||||
* automatically deallocated when the containing Buffer object is
|
||||
* destructed, which simplifies functions with many early-exit
|
||||
* cases. To transfer ownership to the caller, swap it with a buffer
|
||||
* that was passed by reference, e.g:
|
||||
*
|
||||
* void my_func(Buffer *ret_buf) {
|
||||
* Buffer local_buf;
|
||||
* ... work on local_buf ...
|
||||
* local_buf.swap(ret_buf);
|
||||
* }
|
||||
*/
|
||||
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer() : _buf(NULL), _size(0) {}
|
||||
~Buffer() { free(); }
|
||||
|
||||
// Allocate a buffer of given 'size'. Deletes any previous contents.
|
||||
void alloc(size_t size);
|
||||
|
||||
// Allocate and load from a file.
|
||||
bool loadFromFile(const char *file);
|
||||
|
||||
#if BOOTX2BIN_WITH_IMAGE4
|
||||
// Allocate and load a payload from an im4p format file, of expected 'type'.
|
||||
bool loadFromIm4p(const char *file, uint32_t type, bool quiet = false);
|
||||
#endif
|
||||
|
||||
// Allocate and load a payload from an img3 format file, of expected 'type'.
|
||||
bool loadFromImg3(const char *file, uint32_t type);
|
||||
|
||||
// Allocate and load a payload, automatically extracting the payload from
|
||||
// an im4p or img3 format file if detected, and otherwise raw. Will
|
||||
// return failure if im4p or img3 is detected but type does not match.
|
||||
bool loadFromAuto(const char *file, uint32_t type);
|
||||
|
||||
void free();
|
||||
void swap(Buffer *other);
|
||||
void crop(size_t newSize);
|
||||
|
||||
void *buf() { return _buf; }
|
||||
const void *buf() const { return _buf; }
|
||||
size_t size() const { return _size; }
|
||||
|
||||
private:
|
||||
char *_buf;
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
#endif // BUFFER_H
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) 2009, 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.
|
||||
*/
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include "DeviceTreePatcher.h"
|
||||
|
||||
struct MemoryMapFileInfo32 {
|
||||
uint32_t paddr;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
struct MemoryMapFileInfo64 {
|
||||
uint64_t paddr;
|
||||
uint64_t length;
|
||||
};
|
||||
|
||||
DeviceTreePatcher::DeviceTreePatcher(char *base, size_t devtree_size)
|
||||
: _base(base), _treesize(devtree_size),
|
||||
_alloc_idx(0) {
|
||||
}
|
||||
|
||||
uint32_t
|
||||
DeviceTreePatcher::GetNumAddressCells(const char *node_name)
|
||||
{
|
||||
DTNodePtr node;
|
||||
if (!FindNode(0, node_name, &node)) {
|
||||
fprintf(stderr, "GetNumAddressCells: couldn't find \"%s\" node.\n", node_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
char addrCellsProp[] = "#address-cells";
|
||||
char *addrCellsPropp = &addrCellsProp[0];
|
||||
uint32_t *addrCellsData, addrCellsPropSize;
|
||||
|
||||
if (!FindProperty(node, &addrCellsPropp, (void**)&addrCellsData, &addrCellsPropSize)) {
|
||||
return 0x1;
|
||||
} else {
|
||||
return *addrCellsData;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceTreePatcher::AllocateMemoryRange(const char *name,
|
||||
uint64_t pmaddr,
|
||||
uint64_t size) {
|
||||
set_device_tree((addr_t) _base, _treesize);
|
||||
bool use_wide_addrs;
|
||||
uint32_t chosenAddrCells = GetNumAddressCells("chosen");
|
||||
|
||||
switch(chosenAddrCells) {
|
||||
case 0x1:
|
||||
use_wide_addrs = false;
|
||||
break;
|
||||
case 0x2:
|
||||
use_wide_addrs = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "AllocateMemoryRange: chosen->#address-cells = %x, which is not valid.\n", chosenAddrCells);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the memory-map node.
|
||||
DTNodePtr memory_map;
|
||||
if (!FindNode(0, "chosen/memory-map", &memory_map)) {
|
||||
fprintf(stderr, "AllocateMemoryRange: failed to find the /chosen/memory-map node\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find an unused memory map node
|
||||
union {
|
||||
MemoryMapFileInfo32 pd32;
|
||||
MemoryMapFileInfo64 pd64;
|
||||
} *propData;
|
||||
|
||||
char mapName[kPropNameLength];
|
||||
snprintf(mapName, kPropNameLength, "MemoryMapReserved-%d", _alloc_idx);
|
||||
char *propName = mapName;
|
||||
|
||||
uint32_t propSize;
|
||||
if (!FindProperty(memory_map,
|
||||
&propName,
|
||||
(void **) &propData,
|
||||
&propSize)) {
|
||||
fprintf(stderr, "AllocateMemoryRange: failed to find a /chosen/memory-map:%s property\n", mapName);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Section %2u: 0x%llx, %llx %s\n",
|
||||
_alloc_idx, pmaddr, size, name);
|
||||
strlcpy(propName, name, kPropNameLength);
|
||||
|
||||
if (use_wide_addrs) {
|
||||
assert(propSize == 16);
|
||||
propData->pd64.paddr = pmaddr;
|
||||
propData->pd64.length = size;
|
||||
} else {
|
||||
assert(propSize == 8);
|
||||
propData->pd32.paddr = (uint32_t)pmaddr;
|
||||
propData->pd32.length = (uint32_t)size;
|
||||
}
|
||||
|
||||
assert(size == (uint32_t)size);
|
||||
++_alloc_idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceTreePatcher::GetPropertyBuffer(const char *node_name,
|
||||
const char *property_name,
|
||||
void **ret_data,
|
||||
uint32_t *ret_size) {
|
||||
DTNodePtr node;
|
||||
if (!FindNode(0, node_name, &node)) {
|
||||
fprintf(stderr, "Couldn't find node %s\n", node_name);
|
||||
return false;
|
||||
}
|
||||
void *data;
|
||||
uint32_t size;
|
||||
if (!FindProperty(node, const_cast<char **>(&property_name), &data, &size)) {
|
||||
fprintf(stderr, "Couldn't find property %s:%s\n", node_name, property_name);
|
||||
return false;
|
||||
}
|
||||
*ret_data = data;
|
||||
*ret_size = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceTreePatcher::SetPropertyInt(const char *node_name,
|
||||
const char *property_name,
|
||||
uint64_t value) {
|
||||
void *data;
|
||||
uint32_t size;
|
||||
if (!GetPropertyBuffer(node_name, property_name, &data, &size)) return false;
|
||||
// Little-endian properties.
|
||||
char *p = (char *) data;
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
p[i] = value & 255;
|
||||
value >>= 8;
|
||||
}
|
||||
if (value != 0) {
|
||||
fprintf(stderr,
|
||||
"Property %s:%s size %u too small for value 0x%" PRIX64 "\n",
|
||||
node_name, property_name, (unsigned) size, value);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceTreePatcher::SetPropertyTwoQuads(const char *node_name, const char *property_name, uint64_t *values)
|
||||
{
|
||||
void *data;
|
||||
uint32_t size;
|
||||
uint64_t value;
|
||||
if (!GetPropertyBuffer(node_name, property_name, &data, &size)) return false;
|
||||
|
||||
if (size != 16) {
|
||||
fprintf(stderr,
|
||||
"Property %s:%s size %u too small for value 0x%016llx,0x%016llx\n",
|
||||
node_name, property_name, (unsigned) size, values[0], values[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Little-endian properties.
|
||||
char *p = (char *) data;
|
||||
value = values[0];
|
||||
for (uint32_t i = 0; i < size/2; ++i) {
|
||||
p[i] = value & 255;
|
||||
value >>= 8;
|
||||
}
|
||||
|
||||
value = values[1];
|
||||
for (uint32_t i = 0; i < size/2; ++i) {
|
||||
p[i+8] = value & 255;
|
||||
value >>= 8;
|
||||
}
|
||||
|
||||
if (value != 0) {
|
||||
fprintf(stderr,
|
||||
"Property %s:%s size %u too small for value 0x%" PRIX64 "\n",
|
||||
node_name, property_name, (unsigned) size, value);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool DeviceTreePatcher::SetPropertyString(const char *node_name,
|
||||
const char *property_name,
|
||||
const char *s) {
|
||||
void *data;
|
||||
uint32_t size;
|
||||
if (!GetPropertyBuffer(node_name, property_name, &data, &size)) return false;
|
||||
char *p = (char *) data;
|
||||
uint32_t i;
|
||||
for (i = 0; i < size; ++i) {
|
||||
p[i] = s[i];
|
||||
if (s[i] == '\0') break;
|
||||
}
|
||||
if (i == size && s[i] != '\0') {
|
||||
fprintf(stderr,
|
||||
"Property %s:%s size %u too small for string of length %u\n",
|
||||
node_name, property_name, (unsigned) size,
|
||||
(unsigned) strlen(s) + 1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2009,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.
|
||||
*/
|
||||
|
||||
#ifndef DEVICE_TREE_PATCHER_H
|
||||
#define DEVICE_TREE_PATCHER_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Helper for patching a device tree. Beware that this modifies
|
||||
// globals (e.g gDeviceTree) as a side-effect of its methods.
|
||||
class DeviceTreePatcher {
|
||||
public:
|
||||
DeviceTreePatcher(char *base, size_t devtree_size);
|
||||
|
||||
// Use one of the reserved chosen/memory-map nodes to record the location
|
||||
// of a loaded or allocated memory section. Returns true on success, or
|
||||
// false if this fails to claim an unused reserved node.
|
||||
bool AllocateMemoryRange(const char *name, uint64_t pmaddr, uint64_t size);
|
||||
|
||||
// Set an integer property. Returns true on success, or false if the
|
||||
// property cannot be found, or the value is too large to fit.
|
||||
bool SetPropertyInt(const char *node, const char *property, uint64_t value);
|
||||
|
||||
// Set an integer property. Returns true on success, or false if the
|
||||
// property cannot be found, or the value is too large to fit.
|
||||
bool SetPropertyTwoQuads(const char *node, const char *property, uint64_t *value);
|
||||
|
||||
// Set a string property. Returns true on success, or false if the
|
||||
// property cannot be found, or the value is too large to fit.
|
||||
bool SetPropertyString(const char *node, const char *property, const char *s);
|
||||
|
||||
uint32_t GetNumAddressCells(const char *node_name);
|
||||
|
||||
private:
|
||||
// Get a pointer to the storage area for a property. Returns true
|
||||
// and fills in 'data' and 'size' if found. Returns false and does
|
||||
// not modify the return arguments if not found.
|
||||
bool GetPropertyBuffer(const char *node_name,
|
||||
const char *property_name,
|
||||
void **data,
|
||||
uint32_t *size);
|
||||
|
||||
char *_base;
|
||||
size_t _treesize;
|
||||
uint32_t _alloc_idx;
|
||||
};
|
||||
|
||||
#endif // DEVICE_TREE_PATCHER_H
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sys.h"
|
||||
#include "Buffer.h"
|
||||
#include "Kernelcache.h"
|
||||
#include "lib/cksum.h"
|
||||
#include "lib/lzss.h"
|
||||
|
||||
/* Constant for the magic field of the mach_header (32-bit architectures) */
|
||||
#define MH_MAGIC 0xfeedface /* the mach magic number */
|
||||
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
|
||||
|
||||
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[];
|
||||
} __attribute__((packed));
|
||||
|
||||
bool DecompressKernelcache(const Buffer &kernelcache, Buffer *ret_macho) {
|
||||
if (kernelcache.size() < sizeof(compressed_kernel_header)) {
|
||||
fprintf(stderr, "Runt image (only %u bytes)\n",
|
||||
(unsigned) kernelcache.size());
|
||||
return false;
|
||||
}
|
||||
compressed_kernel_header *header =
|
||||
(compressed_kernel_header *) kernelcache.buf();
|
||||
if (ntohl(header->signature) != 'comp' ||
|
||||
ntohl(header->compress_type) != 'lzss') {
|
||||
fprintf(stderr, "File does not contain a compressed kernelcache\n");
|
||||
return false;
|
||||
}
|
||||
fprintf(stderr, "Found compressed kernelcache header\n");
|
||||
uint32_t decompressed_size = ntohl(header->uncompressed_size);
|
||||
uint32_t compressed_size = ntohl(header->compressed_size);
|
||||
if (compressed_size + sizeof(compressed_kernel_header) > kernelcache.size()) {
|
||||
fprintf(stderr, "Compressed image size (%u) greater than image (%u)\n",
|
||||
(unsigned) compressed_size,
|
||||
(unsigned) (kernelcache.size() - sizeof(compressed_kernel_header)));
|
||||
return false;
|
||||
}
|
||||
fprintf(stderr, "Decompress %u -> %u\n", compressed_size, decompressed_size);
|
||||
Buffer decompressed;
|
||||
decompressed.alloc(decompressed_size);
|
||||
uint32_t actual_size = decompress_lzss((uint8_t *) decompressed.buf(),
|
||||
decompressed.size(),
|
||||
(uint8_t *) &header->data[0],
|
||||
compressed_size);
|
||||
if (actual_size != decompressed.size()) {
|
||||
fprintf(stderr, "Decompressed size mismatch, expected %u got %u\n",
|
||||
(unsigned) decompressed.size(), (unsigned) actual_size);
|
||||
return false;
|
||||
}
|
||||
if (adler32((const uint8_t *) decompressed.buf(),
|
||||
decompressed_size) != ntohl(header->adler32)) {
|
||||
fprintf(stderr, "Adler32 mismatch\n");
|
||||
return false;
|
||||
}
|
||||
uint32_t macho_got_magic;
|
||||
memcpy(&macho_got_magic, decompressed.buf(), 4);
|
||||
if (macho_got_magic != MH_MAGIC && macho_got_magic != MH_MAGIC_64) {
|
||||
fprintf(stderr, "Decompressed kernelcache not in Mach-O format\n");
|
||||
return false;
|
||||
}
|
||||
decompressed.swap(ret_macho);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.
|
||||
*/
|
||||
|
||||
#ifndef KERNELCACHE_H
|
||||
#define KERNELCACHE_H 1
|
||||
|
||||
class Buffer;
|
||||
|
||||
bool DecompressKernelcache(const Buffer &kernelcache, Buffer *ret_macho);
|
||||
|
||||
#endif // KERNELCACHE_H
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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 <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sys.h"
|
||||
#include "Buffer.h"
|
||||
#include "LoadMachO.h"
|
||||
|
||||
#define PACKED __attribute__((packed))
|
||||
|
||||
// mach-o/fat.h
|
||||
|
||||
typedef uint32_t cpu_type_t;
|
||||
typedef uint32_t cpu_subtype_t;
|
||||
|
||||
struct load_command {
|
||||
uint32_t cmd;
|
||||
uint32_t cmdsize;
|
||||
};
|
||||
|
||||
// mach-o/loader.h
|
||||
|
||||
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 */
|
||||
} PACKED;
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
/* Constant for the magic field of the mach_header (32-bit architectures) */
|
||||
#define MH_MAGIC 0xfeedface /* the mach magic number */
|
||||
#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
|
||||
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
|
||||
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
|
||||
|
||||
/* 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_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */
|
||||
|
||||
#define LC_THREAD 0x4 /* thread */
|
||||
#define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */
|
||||
#define LC_SEGMENT_64 0x19
|
||||
|
||||
typedef int32_t vm_prot_t;
|
||||
|
||||
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 */
|
||||
} PACKED;
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
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 */
|
||||
/* ... */
|
||||
} PACKED;
|
||||
|
||||
// arm/_types.h
|
||||
|
||||
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 */
|
||||
} PACKED;
|
||||
|
||||
struct arm_thread_state_64 {
|
||||
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 */
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
LoadMachO::LoadMachO(const Buffer &macho,
|
||||
Buffer *ram)
|
||||
: _segment_list(),
|
||||
_last_address(0),
|
||||
_entry_point_phys_offset(0),
|
||||
_macho(macho),
|
||||
_virtual_base(0),
|
||||
_have_virtual_base(false),
|
||||
_ram(ram) {
|
||||
}
|
||||
|
||||
void LoadMachO::OverrideVirtualBase(uint64_t virtual_base) {
|
||||
_virtual_base = virtual_base;
|
||||
_have_virtual_base = true;
|
||||
}
|
||||
|
||||
bool LoadMachO::GetVirtualBase(uint64_t *virtual_base) const {
|
||||
if (_have_virtual_base) {
|
||||
*virtual_base = _virtual_base;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadMachO::Load() {
|
||||
struct mach_header *mH = (struct mach_header *) _macho.buf();
|
||||
uint32_t ncmds = mH->ncmds;
|
||||
char *cmdBase;
|
||||
|
||||
_is_64_bit = mH->magic == MH_MAGIC_64 ? true : false;
|
||||
if (_is_64_bit) {
|
||||
cmdBase = (char *) (((struct mach_header_64*)mH) + 1);
|
||||
} else {
|
||||
cmdBase = (char *) (mH + 1);
|
||||
}
|
||||
|
||||
for (uint32_t cnt = 0; cnt < ncmds; cnt++) {
|
||||
uint32_t cmd, cmdsize;
|
||||
memcpy(&cmd, cmdBase, 4);
|
||||
memcpy(&cmdsize, cmdBase + 4, 4);
|
||||
bool ok;
|
||||
|
||||
//fprintf(stdout, "Current command has cmd %x, size %d\n", cmd, cmdsize);
|
||||
|
||||
switch (cmd) {
|
||||
case LC_SEGMENT:
|
||||
if (_is_64_bit) {
|
||||
fprintf(stderr, "Found LC_SEGMENT in 64-bit Mach-O?");
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
// Copy a segment into ram.
|
||||
ok = DecodeSegment(cmdBase);
|
||||
break;
|
||||
case LC_SEGMENT_64:
|
||||
if (!_is_64_bit) {
|
||||
fprintf(stderr, "Found LC_SEGMENT64 in 32-bit Mach-O?");
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
// Copy a segment into ram.
|
||||
ok = DecodeSegment(cmdBase);
|
||||
break;
|
||||
case LC_UNIXTHREAD:
|
||||
// Get entry point.
|
||||
ok = DecodeUnixThread(cmdBase);
|
||||
break;
|
||||
|
||||
case LC_SYMTAB:
|
||||
// Don't do anything useful with symbol tables.
|
||||
ok = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Ignoring cmd type %d.\n", (int) cmd);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) return false;
|
||||
|
||||
cmdBase += cmdsize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
widen_segment_command(const struct segment_command *scp32,
|
||||
struct segment_command_64 *scp)
|
||||
{
|
||||
scp->cmd = scp32->cmd;
|
||||
scp->cmdsize = scp32->cmdsize;
|
||||
bcopy(scp32->segname, scp->segname, sizeof(scp->segname));
|
||||
scp->vmaddr = scp32->vmaddr;
|
||||
scp->vmsize = scp32->vmsize;
|
||||
scp->fileoff = scp32->fileoff;
|
||||
scp->filesize = scp32->filesize;
|
||||
scp->maxprot = scp32->maxprot;
|
||||
scp->initprot = scp32->initprot;
|
||||
scp->nsects = scp32->nsects;
|
||||
scp->flags = scp32->flags;
|
||||
}
|
||||
|
||||
|
||||
bool LoadMachO::DecodeSegment(const char *cmdBase) {
|
||||
const char *src;
|
||||
char *dst;
|
||||
uint64_t phys_offset;
|
||||
uint64_t vmsize, filesize;
|
||||
struct segment_command_64 segment_command, *scp;
|
||||
size_t segment_command_size;
|
||||
struct load_command *lcp = (struct load_command*)cmdBase;
|
||||
|
||||
if (LC_SEGMENT_64 == lcp->cmd) {
|
||||
segment_command_size = sizeof(struct segment_command_64);
|
||||
} else {
|
||||
segment_command_size = sizeof(struct segment_command);
|
||||
}
|
||||
|
||||
if (lcp->cmdsize < segment_command_size)
|
||||
return false;
|
||||
|
||||
if (LC_SEGMENT_64 == lcp->cmd) {
|
||||
scp = (struct segment_command_64 *)lcp;
|
||||
} else {
|
||||
scp = &segment_command;
|
||||
widen_segment_command((struct segment_command *)cmdBase, scp);
|
||||
}
|
||||
|
||||
if (!_have_virtual_base) {
|
||||
// Assume the virtual address base is the top bits of the first
|
||||
// segment we encounter, aligned to 2MB
|
||||
_have_virtual_base = true;
|
||||
_virtual_base = scp->vmaddr;
|
||||
_virtual_base -= _virtual_base % (1<<21);
|
||||
fprintf(stderr, "inferring virtual base is 0x%016llx\n", _virtual_base);
|
||||
}
|
||||
|
||||
phys_offset = scp->vmaddr - _virtual_base;
|
||||
vmsize = scp->vmsize;
|
||||
|
||||
fprintf(stdout, "Handling %d-bit segment with vmaddr %llx, vmsize %llx, fileoff %lld, filesize %lld, maxprot %d, nsects %d\n",
|
||||
lcp->cmd == LC_SEGMENT_64 ? 64 : 32, scp->vmaddr, scp->vmsize, scp->fileoff, scp->filesize, scp->maxprot, scp->nsects);
|
||||
fprintf(stdout, "Placing at phys offset %llx\n", phys_offset);
|
||||
|
||||
if (phys_offset + vmsize > _ram->size()) {
|
||||
fprintf(stderr, "segment overruns end of memory (0x%016llx vs 0x%08x)\n",
|
||||
(phys_offset + vmsize),
|
||||
(unsigned)_ram->size());
|
||||
return false;
|
||||
}
|
||||
|
||||
src = (const char *) _macho.buf() + scp->fileoff;
|
||||
dst = (char *) _ram->buf() + phys_offset;
|
||||
filesize = scp->filesize;
|
||||
if (scp->fileoff + filesize > _macho.size()) {
|
||||
fprintf(stderr,
|
||||
"segment in Mach-O image past end of file (0x%016llx vs 0x%08x)\n",
|
||||
(scp->fileoff + filesize),
|
||||
(unsigned) _macho.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t cpysize = std::min(filesize, vmsize);
|
||||
assert(cpysize == (size_t)cpysize);
|
||||
memcpy(dst, src, (size_t)cpysize);
|
||||
|
||||
if (vmsize > filesize) {
|
||||
assert((vmsize - filesize) == (size_t)(vmsize - filesize));
|
||||
memset(dst + filesize, 0, (size_t)(vmsize - filesize));
|
||||
}
|
||||
|
||||
// Adjust the last address used by the kernel
|
||||
if (phys_offset + vmsize > _last_address) {
|
||||
_last_address = phys_offset + vmsize;
|
||||
}
|
||||
|
||||
// Record the segment in the list.
|
||||
Segment segment;
|
||||
segment.name = scp->segname;
|
||||
segment.pmaddr = phys_offset;
|
||||
segment.size = vmsize;
|
||||
_segment_list.push_back(segment);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadMachO::DecodeUnixThread(const char *cmdBase) {
|
||||
// The ARM Thread State starts after the thread command stuct plus,
|
||||
// 2 longs for the flaver an num longs.
|
||||
if (!_have_virtual_base) {
|
||||
fprintf(stderr,
|
||||
"got unix thread load command but don't know the virtual base\n");
|
||||
return false;
|
||||
}
|
||||
if (_is_64_bit) {
|
||||
const struct arm_thread_state_64 *armThreadState =
|
||||
(const struct arm_thread_state_64 *)
|
||||
(cmdBase + sizeof(struct thread_command) + 8);
|
||||
_entry_point_phys_offset = armThreadState->pc - _virtual_base;
|
||||
fprintf(stdout, "Setting 64-bit thread entry point (phys offset) to 0x%llx (pc %llx)\n", _entry_point_phys_offset, armThreadState->pc);
|
||||
} else {
|
||||
const struct arm_thread_state *armThreadState =
|
||||
(const struct arm_thread_state *)
|
||||
(cmdBase + sizeof(struct thread_command) + 8);
|
||||
_entry_point_phys_offset = armThreadState->pc - _virtual_base;
|
||||
fprintf(stdout, "Setting 32-bit thread entry point (phys offset) to 0x%x (pc %x)\n", (unsigned)_entry_point_phys_offset, armThreadState->pc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.
|
||||
*/
|
||||
|
||||
#ifndef LOAD_MACH_O_H
|
||||
#define LOAD_MACH_O_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
class Buffer;
|
||||
|
||||
class LoadMachO {
|
||||
public:
|
||||
struct Segment {
|
||||
std::string name;
|
||||
uint64_t pmaddr;
|
||||
uint64_t size;
|
||||
};
|
||||
typedef std::list<Segment> SegmentList;
|
||||
typedef SegmentList::iterator SegmentListIterator;
|
||||
|
||||
LoadMachO(const Buffer &macho, Buffer *ram);
|
||||
void OverrideVirtualBase(uint64_t virtual_base);
|
||||
bool GetVirtualBase(uint64_t *virtual_base) const;
|
||||
bool Load();
|
||||
|
||||
SegmentListIterator SegmentListBegin() { return _segment_list.begin(); }
|
||||
SegmentListIterator SegmentListEnd() { return _segment_list.end(); }
|
||||
uint64_t LastAddress() const { return _last_address; }
|
||||
uint64_t EntryPointPhysOffset() const { return _entry_point_phys_offset; }
|
||||
bool Is64Bit() const { return _is_64_bit; }
|
||||
|
||||
private:
|
||||
bool DecodeSegment(const char *cmdBase);
|
||||
bool DecodeUnixThread(const char *cmdBase);
|
||||
|
||||
SegmentList _segment_list;
|
||||
uint64_t _last_address;
|
||||
uint64_t _entry_point_phys_offset;
|
||||
const Buffer &_macho;
|
||||
uint64_t _virtual_base;
|
||||
bool _have_virtual_base;
|
||||
bool _is_64_bit;
|
||||
Buffer *_ram;
|
||||
};
|
||||
|
||||
#endif // LOAD_MACH_O_H
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.
|
||||
*/
|
||||
/*
|
||||
* Extract the interesting parts of a Mach-O binary into a preloaded memory image.
|
||||
*
|
||||
* We assume that we want all of the __TEXT segment, and anything in __DATA that
|
||||
* isn't zero-filled.
|
||||
*
|
||||
* We expect that the linker will have laid out the data in this object file with
|
||||
* sections appropriately sized and aligned for directly transferring to memory.
|
||||
* This is normally the case for MH_PRELOAD objects.
|
||||
*
|
||||
* Currently only supports 32-bit Mach-O.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <mach-o/loader.h>
|
||||
|
||||
void output_sections(int fd, unsigned char *buf, struct section *sect, uint32_t nsects);
|
||||
void usage(void);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int src, dst;
|
||||
unsigned char *buf;
|
||||
struct mach_header *mh;
|
||||
struct stat s;
|
||||
unsigned char *cursor;
|
||||
uint32_t cmdcount;
|
||||
struct load_command *cmd;
|
||||
struct segment_command *seg;
|
||||
|
||||
/* check args, open files */
|
||||
if (3 != argc)
|
||||
usage();
|
||||
if ((src = open(argv[1], O_RDONLY)) < 0)
|
||||
err(1, "can't open '%s'", argv[1]);
|
||||
if (!strcmp(argv[2], "-")) {
|
||||
dst = STDOUT_FILENO;
|
||||
} else {
|
||||
if ((dst = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0)
|
||||
err(1, "can't open '%s'", argv[2]);
|
||||
}
|
||||
|
||||
/* read the source file */
|
||||
if (fstat(src, &s) < 0)
|
||||
err(1, "can't stat '%s'", argv[1]);
|
||||
if (NULL == (buf = (unsigned char *)malloc(s.st_size)))
|
||||
errx(1, "can't allocate %llu bytes for input file", s.st_size);
|
||||
if (read(src, buf, s.st_size) != s.st_size)
|
||||
err(1, "can't read %llu bytes from input file", s.st_size);
|
||||
close(src);
|
||||
|
||||
/* do some quick sanity on the input */
|
||||
mh = (struct mach_header *)buf;
|
||||
if (mh->magic != MH_MAGIC)
|
||||
errx(1, "unsupported file type (magic 0x%08x)", mh->magic);
|
||||
if (mh->filetype != MH_PRELOAD)
|
||||
errx(1, "unsupported file type (filetype 0x%08x)", mh->filetype);
|
||||
if (!(mh->flags & MH_NOUNDEFS))
|
||||
errx(1, "file has undefined symbols");
|
||||
|
||||
/* process load commands */
|
||||
cursor = (unsigned char *)(mh + 1);
|
||||
cmdcount = mh->ncmds;
|
||||
|
||||
while (cmdcount-- > 0) {
|
||||
cmd = (struct load_command *)cursor;
|
||||
|
||||
/* is this a segment command? */
|
||||
if (cmd->cmd == LC_SEGMENT) {
|
||||
seg = (struct segment_command *)cmd;
|
||||
if (!strcmp("__TEXT", seg->segname)) {
|
||||
/* emit all of the sections in this segment, back to back */
|
||||
output_sections(dst, buf, (struct section *)(seg + 1), seg->nsects);
|
||||
|
||||
}
|
||||
if (!strcmp("__DATA", seg->segname)) {
|
||||
/* emit sections in this segment, stopping if/when we hit a zero-fill section */
|
||||
output_sections(dst, buf, (struct section *)(seg + 1), seg->nsects);
|
||||
}
|
||||
}
|
||||
cursor += cmd->cmdsize;
|
||||
}
|
||||
free(buf);
|
||||
if (STDOUT_FILENO != dst)
|
||||
close(dst);
|
||||
return(0);
|
||||
}
|
||||
|
||||
void
|
||||
output_sections(int fd, unsigned char *buf, struct section *sect, uint32_t nsects)
|
||||
{
|
||||
|
||||
while (nsects-- > 0) {
|
||||
/* some linkers forget to set the S_ZEROFILL flag on zerofill sections */
|
||||
if ((sect->flags & S_ZEROFILL) ||
|
||||
!strcmp(sect->sectname, "__common") ||
|
||||
!strcmp(sect->sectname, "__bss"))
|
||||
return;
|
||||
|
||||
//warnx("writing %s,%s", sect->segname, sect->sectname);
|
||||
|
||||
/* write section data from memory to output */
|
||||
if (write(fd, buf + sect->offset, sect->size) != sect->size)
|
||||
err(1, "error writing to output file");
|
||||
sect++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
warnx("usage:");
|
||||
fprintf(stderr, "\n %s <mach-o file> {<binary file>|-}\n\n", getprogname());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
# Copyright (C) 2007 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.
|
||||
|
||||
# Makefile for build/install-time tools
|
||||
|
||||
include makefiles/macros.mk
|
||||
|
||||
HOST_ARCH := $(shell uname -m)
|
||||
BHC := $(call TOTOOLSDIR,bhc)
|
||||
MACHO2BIN := $(call TOTOOLSDIR,Macho2Bin)
|
||||
BOOTX2BIN := $(call TOTOOLSDIR,BootX2Bin)
|
||||
|
||||
# Host libImg4Decode.a is *sometimes* installed to your root filesystem.
|
||||
# Sometimes it's in your platform. Who knows. Who cares. Let's try a
|
||||
# few places because I'm sick of filling in TPS reports to get it fixed.
|
||||
|
||||
HOST_IMG4_SEARCH := \
|
||||
$(PLATFORMROOT)/usr/local \
|
||||
$(PLATFORMROOT)/../../../usr/local \
|
||||
/usr/local
|
||||
|
||||
HOST_IMG4_FOUND := \
|
||||
$(firstword $(foreach path,\
|
||||
$(HOST_IMG4_SEARCH),\
|
||||
$(if $(realpath $(path)/include/amd/Img4Decode.h),\
|
||||
$(realpath $(path)))))
|
||||
|
||||
HOST_IMG4_INCLUDE := $(HOST_IMG4_FOUND)/include/amd/Img4Decode.h
|
||||
HOST_IMG4_LIB := $(HOST_IMG4_FOUND)/lib/amd/libImg4Decode.a
|
||||
ifeq ($(and $(realpath $(HOST_IMG4_INCLUDE)),$(realpath $(HOST_IMG4_LIB))),)
|
||||
$(info Host Image4 installation broken. Something is missing.)
|
||||
HOST_IMG4_FOUND :=
|
||||
endif
|
||||
|
||||
ifneq ($(HOST_IMG4_FOUND),)
|
||||
$(info Host Image4 installation found under $(HOST_IMG4_FOUND))
|
||||
else
|
||||
$(info Host Image4 installation not found, omitting support)
|
||||
endif
|
||||
|
||||
BOOTX2BIN_INCLUDES := . include lib/pki
|
||||
BOOTX2BIN_PREBUILT :=
|
||||
BOOTX2BIN_FLAGS := -Wall -O -g -Wformat -DDEBUG
|
||||
|
||||
# Optional Image4 support.
|
||||
ifneq ($(HOST_IMG4_FOUND),)
|
||||
BOOTX2BIN_FLAGS += -DBOOTX2BIN_WITH_IMAGE4=1
|
||||
BOOTX2BIN_INCLUDES += $(HOST_IMG4_FOUND)/include/amd
|
||||
BOOTX2BIN_PREBUILT += $(HOST_IMG4_LIB)
|
||||
endif
|
||||
|
||||
# Add includes to flags.
|
||||
BOOTX2BIN_FLAGS += $(foreach inc,$(BOOTX2BIN_INCLUDES),-I$(inc))
|
||||
|
||||
build-tools: destdir $(BHC) $(MACHO2BIN) $(BOOTX2BIN)
|
||||
|
||||
install-tools: destdir $(BHC) $(MACHO2BIN) $(BOOTX2BIN)
|
||||
|
||||
clean-tools:
|
||||
@rm -f $(BHC)
|
||||
@rm -f $(MACHO2BIN)
|
||||
@rm -f $(BOOTX2BIN)
|
||||
@rm -rf $(TOOLS_BIN)
|
||||
|
||||
destdir:
|
||||
$(v_)-mkdir -p $(TOOLS_BIN)
|
||||
|
||||
|
||||
$(BHC): $(SRCROOT)/tools/bhc.c
|
||||
@echo HOST_CC $@
|
||||
$(_v)${HOST_CC} -isysroot $(HOST_SDKROOT) -o $@ $<
|
||||
|
||||
$(MACHO2BIN): $(SRCROOT)/tools/Macho2Bin.c
|
||||
@echo HOST_CC $@
|
||||
$(_v)${HOST_CC} -isysroot $(HOST_SDKROOT) -o $@ $<
|
||||
|
||||
$(call TOTOOLSDIR,%.o): $(SRCROOT)/%.c
|
||||
@echo HOST_CC $@
|
||||
@mkdir -p $(dir $@)
|
||||
$(_v)${HOST_CC} -isysroot $(HOST_SDKROOT) -c -o $@ $< $(BOOTX2BIN_FLAGS)
|
||||
|
||||
$(call TOTOOLSDIR,%.o): $(SRCROOT)/%.cpp
|
||||
@echo HOST_CPP $@
|
||||
@mkdir -p $(dir $@)
|
||||
$(_v)${HOST_CPP} -isysroot $(HOST_SDKROOT) -c -o $@ $< $(BOOTX2BIN_FLAGS)
|
||||
|
||||
LIBDER_SRCS := \
|
||||
lib/pki/libDER/DER_CertCrl.c \
|
||||
lib/pki/libDER/DER_Decode.c \
|
||||
lib/pki/libDER/DER_Digest.c \
|
||||
lib/pki/libDER/DER_Encode.c \
|
||||
lib/pki/libDER/DER_Keys.c \
|
||||
lib/pki/libDER/oids.c
|
||||
|
||||
BOOTX2BIN_SRCS := \
|
||||
tools/BootX2Bin.cpp \
|
||||
tools/Buffer.cpp \
|
||||
tools/DeviceTreePatcher.cpp \
|
||||
tools/Kernelcache.cpp \
|
||||
tools/LoadMachO.cpp \
|
||||
drivers/sha1/mozilla_sha.c \
|
||||
drivers/sha1/sha1.c \
|
||||
lib/cksum/adler32.c \
|
||||
lib/devicetree/devicetree.c \
|
||||
lib/image/image3/Image3.c \
|
||||
lib/lzss/lzss.c \
|
||||
$(LIBDER_SRCS) \
|
||||
$(NULL)
|
||||
BOOTX2BIN_OBJS := $(patsubst %.c,$(call TOTOOLSDIR,%.o),$(BOOTX2BIN_SRCS))
|
||||
BOOTX2BIN_OBJS := $(patsubst %.cpp,$(call TOTOOLSDIR,%.o),$(BOOTX2BIN_OBJS))
|
||||
|
||||
$(BOOTX2BIN): $(BOOTX2BIN_OBJS) $(BOOTX2BIN_PREBUILT)
|
||||
@echo HOST_LD $@
|
||||
$(_v)${HOST_CPP} -isysroot $(HOST_SDKROOT) -o $@ $^
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import linecache
|
||||
import cgi
|
||||
import operator
|
||||
import xml.sax.saxutils as saxutils
|
||||
|
||||
|
||||
Usage='''
|
||||
analyzer-report build_output_dir suppressfile
|
||||
'''
|
||||
ignored_hashes = None
|
||||
def get_source_line(filename, linenum):
|
||||
return linecache.getline(filename, int(linenum)).strip();
|
||||
|
||||
def process_report_file(filename):
|
||||
buginfo={}
|
||||
def populate(comment_name, friendly_name, line):
|
||||
if friendly_name not in buginfo and comment_name in line:
|
||||
front_slice = len("<!-- %s " % comment_name)
|
||||
end_slice = len(" -->")
|
||||
buginfo[friendly_name] = line[front_slice:-end_slice].strip()
|
||||
return True;
|
||||
return False;
|
||||
actions = { "*****Not a filename*******": "report_file", "BUGDESC": "description", "BUGTYPE": "type",
|
||||
"BUGCATEGORY": "category", "BUGFILE": "source_file",
|
||||
"BUGLINE": "line_num"}
|
||||
buginfo['report_file'] = filename
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
try:
|
||||
for (cn, fn) in actions.items():
|
||||
if populate(cn, fn, line): raise ValueError, "Continue"
|
||||
except ValueError: continue # Lazy hack. Nothing in here uses ValueError.
|
||||
if set(actions.values()) == set(buginfo.keys()):
|
||||
buginfo["source_line"] = get_source_line(buginfo['source_file'], buginfo['line_num']);
|
||||
return buginfo
|
||||
return None
|
||||
|
||||
|
||||
'''
|
||||
Turns a buginfo dict into a "stable" bug hash. This is meant to be used to
|
||||
mark problems as ignored.
|
||||
'''
|
||||
def bughash(buginfo):
|
||||
return "[%s # %s # %s]" % (buginfo['description'], buginfo['source_line'], os.path.basename(buginfo['source_file']))
|
||||
|
||||
|
||||
def generate_report_file(buginfos, misc, outfile):
|
||||
with(open(outfile, "w")) as out:
|
||||
print >>out, "<html><head><title>Clang analyzer report for %s</title></head>" % sys.argv[1]
|
||||
print >>out, "<table border=1><tr><th>Source file<th>Bug description</th><th>bughash</th>"
|
||||
for bug in sorted(buginfos, key=operator.itemgetter('source_file', 'description')):
|
||||
if is_ignored(bughash(bug)): continue
|
||||
print >>out, " <tr>\n"
|
||||
print >>out, " <td><a href={}>{}</a></td>\n".format(saxutils.quoteattr(bug['report_file']), saxutils.escape(bug['source_file']))
|
||||
print >>out, " <td>{}</td>\n".format(saxutils.escape(bug['description']))
|
||||
print >>out, " <td><input size=30 readonly=\"true\" value={}></input></td>\n".format(saxutils.quoteattr(bughash(bug)))
|
||||
print >>out, " </tr>\n"
|
||||
print >>out, "</table><hr><pre>%s</pre></html>" % saxutils.escape(misc)
|
||||
def is_ignored(i):
|
||||
global ignored_hashes
|
||||
if ignored_hashes is None:
|
||||
ignored_hashes = set()
|
||||
for line in open(ignorefile):
|
||||
if line.strip() and not line.startswith("#"):
|
||||
ignored_hashes.add(line.strip())
|
||||
return i in ignored_hashes
|
||||
def run_misc(buginfos):
|
||||
import cStringIO
|
||||
s = cStringIO.StringIO()
|
||||
ignored = [bughash(i) for i in buginfos if is_ignored(bughash(i))]
|
||||
not_ignored = [bughash(i) for i in buginfos if not is_ignored(bughash(i))]
|
||||
print >>s, "Ignored %d bug(s) because of suppress file" % len(ignored)
|
||||
|
||||
if ignored_hashes:
|
||||
for h in ignored_hashes:
|
||||
if h not in ignored:
|
||||
print >>s, "Warning: Possible stale bughash in suppress file: %s" % h
|
||||
return s.getvalue()
|
||||
|
||||
ignorefile = os.path.abspath(sys.argv[2])
|
||||
os.chdir(sys.argv[1])
|
||||
buginfos=[]
|
||||
for folder, subs, files in os.walk("."):
|
||||
for fn in files:
|
||||
if fn.endswith(".html"):
|
||||
buginfo = process_report_file(os.path.join(folder,fn))
|
||||
if(buginfo):
|
||||
buginfos.append(buginfo)
|
||||
|
||||
|
||||
generate_report_file(buginfos, run_misc(buginfos), "index.html")
|
||||
@@ -0,0 +1,14 @@
|
||||
# Clang analyzer ignore file:
|
||||
# Lines starting with "#" or blank lines are ignored
|
||||
# "bughashes" are [ error # line_of_code # filename ] pairs
|
||||
# They are printed in a textbox in the report.html file the analyzer prints
|
||||
# out.
|
||||
|
||||
# <rdar://problem/19426972> Clang analyzer report of reading uninitialized value relies on mutual inconsistent assumptions
|
||||
[The right operand of '&' is a garbage value # ret_val |= (cal_bits[(*byte_pos) + 1] & remainder_mask) << space_in_this_byte; # dcs_calibration.c]
|
||||
[Function call argument is an uninitialized value # dprintf(DEBUG_CRITICAL, "gg charger: 0x%x=0x%x 0x%x=0x%x 0x%x=0x%x 0x%x=0x%x 0x%x=0x%x\n", # hdqgauge.c]
|
||||
|
||||
# The following are benign (no security implications) errors in code that's
|
||||
# considered "legacy". If we were actively maintaining this code and using it
|
||||
# in new targets, these issues would be worth fixing, but we're not so we won't
|
||||
[Potential leak of memory pointed to by 'device' # } # effaceable_nand.c]
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
/* bhc.c ...Binary data <-> Intel Hex data Converter Ver 1.0.3 */
|
||||
/* Suport ROM size 16k,32k,64k,128k,256k,512k,1024k */
|
||||
/* 1998/9/12 Kazuhiko Mori(COW) (C)Cow Project Workshop */
|
||||
/* e-mail:cow@he.mirai.ne.jp */
|
||||
/* This Program is Free Soft */
|
||||
/* This version for Unix,Win32,OS/2 */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* function prototype */
|
||||
int b2h(char *in,char *out, char *bufs);
|
||||
int h2b(char *in,char *out, char *bufs);
|
||||
int scnv(char *size_ch);
|
||||
int ch2num(char ch1,char ch2);
|
||||
|
||||
int main(int argc,char *argv[])/*Main routine*/
|
||||
{
|
||||
int i;
|
||||
char inf[255],outf[255],flg[8],bufs[8];
|
||||
|
||||
i = 0;/*Set return code*/
|
||||
printf("bhc Ver1.0.3 Binary data <-> Intel Hex data Converter.\n");
|
||||
printf("(C)1997-1998 Cow Project Workshop.\n");
|
||||
if(argc == 5){ /* Parameter check*/
|
||||
strcpy(flg,argv[1]);
|
||||
strcpy(inf,argv[2]);
|
||||
strcpy(outf,argv[3]);
|
||||
strcpy(bufs,argv[4]);
|
||||
printf("Convert flag %s\nInput file %s\nOutput file %s\n\n",flg,inf,outf);
|
||||
flg[1] = tolower(flg[1]);
|
||||
if(0 == strcmp(flg,"-h")){
|
||||
printf("Making Intel Hex file.\n");
|
||||
i = b2h(inf,outf,bufs);
|
||||
}
|
||||
else {
|
||||
if(0 == strcmp(flg,"-b")){
|
||||
printf("Making Binary file.\n");
|
||||
i = h2b(inf,outf,bufs);
|
||||
}
|
||||
else {
|
||||
printf("Flag Error!\n");
|
||||
i = 3;
|
||||
}
|
||||
}
|
||||
printf("Finished!\n");
|
||||
return i;
|
||||
}
|
||||
else { /*Error Infomation*/
|
||||
printf("Parameter Error!\n\nUsage: bhc flag input_file output_file ROM_size\n\n");
|
||||
printf(" flag: -h Convert to Intel Hex data.\n"
|
||||
" -b Convert to Binary data.\n\n"
|
||||
" ROM_size: 16k,32k,64k,128k,256k,512k,1024k\n\nTry again!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int b2h(char *in,char *out,char *bufs)/*Binary to Hex convert*/
|
||||
{
|
||||
FILE *inp, *outp;
|
||||
int ch,cnt,csum,ofsa;
|
||||
struct stat statbuf;
|
||||
long int fsize,fpoint,fsub,adrs;
|
||||
|
||||
/*Open file check*/
|
||||
if((inp = fopen(in, "rb")) == NULL){
|
||||
printf("Cannot open input file.\n");
|
||||
return 1;
|
||||
}
|
||||
if((outp = fopen(out, "wt")) == NULL){
|
||||
printf("Cannot open output file.\n");
|
||||
return 1;
|
||||
}
|
||||
fstat(fileno(inp), &statbuf);
|
||||
printf("Input file size=%lld\n",statbuf.st_size);
|
||||
fsize = (long int)statbuf.st_size;
|
||||
|
||||
if(fsize > scnv(bufs)){ /* File size check*/
|
||||
printf("File size error.\n");
|
||||
fclose(inp);
|
||||
fclose(outp);
|
||||
return 2;
|
||||
}
|
||||
|
||||
cnt = 0; ofsa = 0; adrs = 0; fpoint = 0;
|
||||
fprintf(outp,":020000020000FC\n");/*Start Header*/
|
||||
fsub = fsize - fpoint;
|
||||
if (fsub > 0x20) {
|
||||
fprintf(outp,":20%04X00",(unsigned int)adrs);/*Hex line Header*/
|
||||
csum = 0x20 + (adrs>>8) + (adrs & 0xFF);
|
||||
adrs = 0x20;
|
||||
}
|
||||
else {
|
||||
fprintf(outp, ":%02X%04X00", (unsigned int)fsub,(unsigned int)adrs);/*Hex line Header*/
|
||||
csum = fsub + (adrs>>8) + (adrs & 0xFF);
|
||||
adrs = fsub;
|
||||
}
|
||||
while (fsub > 0){
|
||||
ch = fgetc(inp);
|
||||
fprintf(outp,"%02X",ch);/*Put data*/
|
||||
cnt++; fpoint++;
|
||||
fsub = fsize - fpoint;
|
||||
csum = ch + csum;
|
||||
if((fsub == 0)||(cnt == 0x20)){
|
||||
cnt = 0; csum = 0xFF & (~csum + 1);
|
||||
fprintf(outp,"%02X\n",csum);/*Put checksum*/
|
||||
if(fsub == 0) break;
|
||||
if(adrs > 0xFFFF){
|
||||
ofsa = 0x1000 + ofsa;
|
||||
adrs = 0;
|
||||
fprintf(outp,":02000002%04X",(unsigned int)ofsa);/*Change offset address*/
|
||||
csum = 0x02 + 0x02 + (ofsa>>8) + (ofsa & 0xFF);
|
||||
csum = 0xFF & (~csum + 1);
|
||||
fprintf(outp,"%02X\n", csum);
|
||||
}
|
||||
adrs = 0xFFFF & adrs;
|
||||
if (fsub > 0x20) {
|
||||
fprintf(outp,":20%04X00",(unsigned int)adrs);/*Next Hex line Header*/
|
||||
csum = 0x20 + (adrs>>8) + (adrs & 0xFF);
|
||||
adrs = adrs + 0x20;
|
||||
}
|
||||
else {
|
||||
if(fsub > 0){
|
||||
fprintf(outp, ":%02X%04X00", (unsigned int)fsub,(unsigned int)adrs);/*Next Hex line Header*/
|
||||
csum = fsub + (adrs>>8) + (adrs & 0xFF);
|
||||
adrs = adrs + fsub;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(outp,":00000001FF\n");/*End footer*/
|
||||
|
||||
fstat(fileno(outp), &statbuf);
|
||||
printf("Output file size=%lld\n",statbuf.st_size);
|
||||
|
||||
fclose(inp);
|
||||
fclose(outp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int h2b(char *in,char *out,char *bufs)/*Hex to Binary convert*/
|
||||
{
|
||||
FILE *inp, *outp;
|
||||
int lsize,dmode;
|
||||
char buf[1048576],lbuf[256];
|
||||
struct stat statbuf;
|
||||
long fsize,po,adrs,ofsa,abadrs;
|
||||
|
||||
/*Open file check*/
|
||||
if((inp = fopen(in, "rt")) == NULL){
|
||||
printf("Cannot open input file.\n");
|
||||
return 1;
|
||||
}
|
||||
if((outp = fopen(out, "wb")) == NULL){
|
||||
printf("Cannot open output file.\n");
|
||||
return 1;
|
||||
}
|
||||
fstat(fileno(inp), &statbuf);
|
||||
printf("Input file size=%lld\n",statbuf.st_size);
|
||||
|
||||
fsize = scnv(bufs);
|
||||
|
||||
for(po = 0;po < 1048576;po++) buf[po] = 0xFF;/*Fill Buffer 0xFF data*/
|
||||
|
||||
adrs = 0; ofsa = 0;
|
||||
while (NULL != fgets(lbuf,254,inp)){/*Loop until end of file*/
|
||||
if((lbuf[0] != ':')||(!isalnum(lbuf[1]))||(!isalnum(lbuf[2]))) {
|
||||
continue;
|
||||
}
|
||||
lsize = ch2num(lbuf[1],lbuf[2]) * 2;
|
||||
adrs = (long)(ch2num(lbuf[3],lbuf[4])) * 0x100L
|
||||
+ (long)(ch2num(lbuf[5],lbuf[6]));
|
||||
if(fsize < (ofsa + adrs)) {/*Address check*/
|
||||
printf("Address error!\n");
|
||||
fclose(inp);
|
||||
fclose(outp);
|
||||
return 5;
|
||||
}
|
||||
|
||||
dmode = ch2num(lbuf[7],lbuf[8]);/*Get data mode */
|
||||
if(dmode == 2) {/*Get offset address*/
|
||||
ofsa = ((long)ch2num(lbuf[9],lbuf[10]) * 0x100L
|
||||
+ (long)ch2num(lbuf[11],lbuf[12])) * 0x10L;
|
||||
continue;
|
||||
}
|
||||
if(dmode == 0){/*Get binary data*/
|
||||
abadrs = ofsa + adrs;
|
||||
for(po = 9;po < (lsize+9);po=po+2){
|
||||
buf[abadrs++] = ch2num(lbuf[po],lbuf[po+1]);/*Write data to buffer*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(po = 0;po < fsize;po++){/*Write buffer data to file*/
|
||||
putc(buf[po],outp);
|
||||
}
|
||||
|
||||
fstat(fileno(outp), &statbuf);
|
||||
printf("Output file size=%lld\n",statbuf.st_size);
|
||||
|
||||
fclose(inp);
|
||||
fclose(outp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scnv(char *size_ch)/* Size character converte to Integer*/
|
||||
{
|
||||
if(strcmp(size_ch,"16k") == 0) return 16384;
|
||||
if(strcmp(size_ch,"32k") == 0) return 32768;
|
||||
if(strcmp(size_ch,"64k") == 0) return 65536;
|
||||
if(strcmp(size_ch,"128k") == 0) return 131072;
|
||||
if(strcmp(size_ch,"256k") == 0) return 262144;
|
||||
if(strcmp(size_ch,"512k") == 0) return 524288;
|
||||
if(strcmp(size_ch,"1024k") == 0) return 1048576;
|
||||
return 1048576;
|
||||
}
|
||||
|
||||
int ch2num(char ch1,char ch2)
|
||||
{
|
||||
int nout;
|
||||
|
||||
if((ch1 >= '0')&&('9' >= ch1)) nout = (ch1 & 0xf) * 0x10;
|
||||
else nout = ((ch1 & 0xf) + 9) * 0x10;
|
||||
if((ch2 >= '0')&&('9' >= ch2)) return (ch2 & 0xf) + nout;
|
||||
else return ((ch2 & 0xf) + 9) + nout;
|
||||
}
|
||||
/* Thanks to KITA (QZE01066@nifty.ne.jp) */
|
||||
/* End of file*/
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python2.7
|
||||
|
||||
# Check generated build/library_list for any library~arch which will be
|
||||
# built with more than one set of flags. The flags are not used to
|
||||
# uniquely identify a built library, so we instead end up with a
|
||||
# non-deterministic build where one set of flags builds last, and ends
|
||||
# up linked into all application/product executables.
|
||||
|
||||
import sys
|
||||
|
||||
def main():
|
||||
lib_flags_sets = {}
|
||||
for line in open(sys.argv[1], 'r').readlines():
|
||||
# Split each line by '~' delimiter
|
||||
parts = line.strip().split('~')
|
||||
# Re-join first two components in target library
|
||||
lib = ' '.join(parts[0:2])
|
||||
# All other components into flags
|
||||
flags = ' '.join(parts[2:])
|
||||
# Note that this lib has these flags in use
|
||||
lib_flags_sets.setdefault(lib, {})[flags] = None
|
||||
# Check if any library is attempting to be built with more than one
|
||||
# set of flags.
|
||||
ok = True
|
||||
for lib, flags_set in lib_flags_sets.items():
|
||||
if len(flags_set) == 1:
|
||||
continue
|
||||
ok = False
|
||||
sys.stderr.write('Multiple flag sets for library %s:\n' % lib)
|
||||
for flags in flags_set.keys():
|
||||
sys.stderr.write(' %s \n' % flags)
|
||||
return 0 if ok else 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# Simple helper script for enforcing file size
|
||||
#
|
||||
# This is a bit too cluttered in a Makefile rule.
|
||||
|
||||
if [ $# -ne 3 ]; then
|
||||
echo "Usage: $0 filename max_size name_of_check"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILENAME=$1
|
||||
MAXSIZE=$(( $2 ))
|
||||
NAME=$3
|
||||
|
||||
SIZE=`stat -f '%z' "$FILENAME"`
|
||||
|
||||
if [ $SIZE -gt $MAXSIZE ]; then
|
||||
echo "[check_product_size $NAME]: $FILENAME is too big! Built size: $SIZE bytes, limit: $MAXSIZE bytes, oversized by $(( $SIZE - $MAXSIZE )) bytes." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[check_product_size $NAME]: $FILENAME PASSED. Usage: $SIZE/$MAXSIZE bytes"
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/tclsh
|
||||
################################################################################
|
||||
#
|
||||
# Given a mach-o binary, use nm and gdb to size all the functions it contains
|
||||
#
|
||||
|
||||
|
||||
proc main { } {
|
||||
global argv
|
||||
|
||||
set binfile [lindex $argv 0]
|
||||
if {![file readable $binfile]} {
|
||||
puts "can't read '$binfile'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# run nm and parse the output
|
||||
do_nm $binfile
|
||||
|
||||
# compute text symbol sizes
|
||||
do_sizing
|
||||
|
||||
# now ask GDB for the file(s) containing each symbol
|
||||
do_gdb $binfile
|
||||
|
||||
# and emit results
|
||||
do_emit_sized
|
||||
do_emit_files
|
||||
}
|
||||
|
||||
|
||||
proc do_nm {binfile} {
|
||||
|
||||
global symbol_addresses
|
||||
global text_symbols
|
||||
|
||||
set lines [exec nm -n $binfile]
|
||||
|
||||
foreach {addr type _sym} $lines {
|
||||
# record the fact that there is a symbol boundary at this address
|
||||
lappend addresses "0x$addr"
|
||||
|
||||
# if the symbol is a text symbol, record its address
|
||||
# this could be "tds" to get data symbols too..
|
||||
if {[string match \[tT\] $type]} {
|
||||
# ignore the first leading underscore in the symbol name
|
||||
set sym [string range $_sym 1 end]
|
||||
|
||||
# save the text symbol and its address
|
||||
set text_symbols($sym) "0x$addr"
|
||||
}
|
||||
}
|
||||
set symbol_addresses [lsort -integer -unique $addresses]
|
||||
puts "[llength [array names text_symbols]] text symbols"
|
||||
}
|
||||
|
||||
|
||||
proc do_sizing {} {
|
||||
|
||||
global symbol_addresses
|
||||
global text_symbols
|
||||
global text_symbol_sizes
|
||||
global text_size_symbols
|
||||
|
||||
# walk the array of text symbols
|
||||
foreach sym [array names text_symbols] {
|
||||
|
||||
# get the address of the symbol
|
||||
set addr $text_symbols($sym)
|
||||
|
||||
# find the address in the global list of symbol addresses
|
||||
set index [lsearch $symbol_addresses $addr]
|
||||
|
||||
# find the next symbol boundary
|
||||
set nextaddr [lindex $symbol_addresses [expr $index + 1]]
|
||||
|
||||
# size is the distance between the two
|
||||
set size [expr $nextaddr - $addr]
|
||||
set text_symbol_sizes($sym) $size
|
||||
|
||||
# index symbols by size
|
||||
lappend text_size_symbols($size) $sym
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
proc do_gdb {binfile} {
|
||||
|
||||
global symbol_filenames
|
||||
global file_symbols
|
||||
|
||||
# ask GDB for the output of "info functions"
|
||||
set lines [exec echo "info functions" | xcrun -sdk iphoneos.internal gdb --quiet $binfile 2>/dev/null]
|
||||
|
||||
# Output from GDB includes lines announcing files, and lines describing functions
|
||||
# within those files.
|
||||
foreach line [split $lines "\n"] {
|
||||
# new file announcement
|
||||
if {[string match "File *" $line]} {
|
||||
set current_file [string range $line 5 end-1]
|
||||
}
|
||||
|
||||
# function within a file
|
||||
set paren [string first "(" $line]
|
||||
if {$paren > 0} {
|
||||
|
||||
# function name is last token before ( in the line
|
||||
set frag [string range $line 0 [expr $paren - 1]]
|
||||
set sym [string trimleft [lindex [split $frag] end] "*"]
|
||||
|
||||
# index filesnames by symbol
|
||||
lappend symbol_filenames($sym) $current_file
|
||||
|
||||
# index symbols by filename
|
||||
lappend file_symbols($current_file) $sym
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
proc do_emit_sized {} {
|
||||
|
||||
global text_size_symbols
|
||||
global symbol_filenames
|
||||
|
||||
puts "Symbols by size"
|
||||
puts "==============="
|
||||
foreach size [lsort -integer [array names text_size_symbols]] {
|
||||
foreach sym $text_size_symbols($size) {
|
||||
if {[llength [array names symbol_filenames -exact $sym]] > 0} {
|
||||
puts [format "%8d %-50s %s" $size $sym $symbol_filenames($sym)]
|
||||
} else {
|
||||
puts [format "%8d %-50s" $size $sym]
|
||||
}
|
||||
}
|
||||
}
|
||||
puts ""
|
||||
}
|
||||
|
||||
proc do_emit_files {} {
|
||||
|
||||
global file_symbols
|
||||
global text_symbol_sizes
|
||||
|
||||
puts "Symbols by file size"
|
||||
puts "===================="
|
||||
foreach fn [array names file_symbols] {
|
||||
set size 0
|
||||
foreach sym $file_symbols($fn) {
|
||||
if {[array names text_symbol_sizes $sym] != ""} {
|
||||
incr size $text_symbol_sizes($sym)
|
||||
} else {
|
||||
puts "WARNING: no size information for '$sym' - nm didn't see it."
|
||||
}
|
||||
}
|
||||
lappend size_files($size) $fn
|
||||
}
|
||||
|
||||
foreach size [lsort -integer [array names size_files]] {
|
||||
foreach fn $size_files($size) {
|
||||
puts "$fn: ($size)"
|
||||
set lines [list]
|
||||
foreach sym $file_symbols($fn) {
|
||||
if {[array names text_symbol_sizes $sym] != ""} {
|
||||
lappend lines [list $text_symbol_sizes($sym) $sym]
|
||||
}
|
||||
}
|
||||
foreach ent [lsort -index 0 -integer $lines] {
|
||||
puts [format "%8d %-50s" [lindex $ent 0] [lindex $ent 1]]
|
||||
}
|
||||
puts ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
main
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/local/bin/astris
|
||||
# -*-Tcl-*-
|
||||
|
||||
# Read out cfg engine state and debug registers
|
||||
|
||||
set soc_cfg_error [mem -memap 4 0x210480000]
|
||||
puts [format "SOC CFG ERROR(0x210480000) 0x%x" $soc_cfg_error]
|
||||
|
||||
set wr_resp_err_in_flight [expr ($soc_cfg_error>>24) & 0x3F]
|
||||
set wr_resp_err [expr ($soc_cfg_error>>20) & 0x3]
|
||||
set rd_timeout_err [expr ($soc_cfg_error>>16) & 0x1]
|
||||
set rd_resp_err [expr ($soc_cfg_error>>12) & 0x3]
|
||||
set unknown_cmd_err [expr ($soc_cfg_error>>8) & 0x1]
|
||||
set spec_access_err [expr ($soc_cfg_error>>4) & 0x3]
|
||||
set table_access_err [expr $soc_cfg_error & 0x3]
|
||||
|
||||
if {$wr_resp_err_in_flight} {puts [format "\twr_resp_err_in_flight: 0x%x" $wr_resp_err_in_flight]}
|
||||
if {$wr_resp_err} {puts [format "\twr_resp_err: 0x%x" $wr_resp_err]}
|
||||
if {$rd_timeout_err} {puts [format "\trd_timeout_err: 0x%x" $rd_timeout_err]}
|
||||
if {$rd_resp_err} {puts [format "\trd_resp_err: 0x%x" $rd_resp_err]}
|
||||
if {$unknown_cmd_err} {puts [format "\tunknown_cmd_err: 0x%x" $unknown_cmd_err]}
|
||||
if {$spec_access_err} {puts [format "\tspec_access_err: 0x%x" $spec_access_err]}
|
||||
if {$table_access_err} {puts [format "\ttable_access_err: 0x%x" $table_access_err]}
|
||||
|
||||
set last_fetch_rd_addr [mem -memap 4 0x21048000c]
|
||||
set last_proc_rd_addr [mem -memap 4 0x210480010]
|
||||
set last_proc_wr_addr [mem -memap 4 0x210480014]
|
||||
puts [format "last_fetch_rd_addr(0x21048000c) 0x%x" $last_fetch_rd_addr]
|
||||
puts [format "last_proc_rd_addr(0x210480010) 0x%x" $last_proc_rd_addr]
|
||||
puts [format "last_proc_wr_addr(0x210480014) 0x%x" $last_proc_wr_addr]
|
||||
|
||||
|
||||
set aop_state [mem -memap 4 0x210000008]
|
||||
puts [format "AOP STATE(0x210000008) 0x%x" $aop_state]
|
||||
set pmu_if [expr $aop_state & 0x1F]
|
||||
if {$pmu_if == 0x0} {puts "\tRESET - cold boot wait state until minipmgr_aop_awake_ack_sync"}
|
||||
if {$pmu_if == 0x1} {puts "\tAWAKE - in the AWAKE state"}
|
||||
if {$pmu_if == 0x2} {puts "\tAWAKE_exit_delay - counter delay before exiting from AWAKE"}
|
||||
if {$pmu_if == 0x3} {puts "\tAWAKE_exit_pre - executing preamble"}
|
||||
if {$pmu_if == 0x4} {puts "\tAWAKE_exit_HS - handshake with miniPMGR"}
|
||||
if {$pmu_if == 0x5} {puts "\tAWAKE_exit_post - executing postamble"}
|
||||
if {$pmu_if == 0x6} {puts "\tAWAKE_exit_PMU - handshake with PMU"}
|
||||
if {$pmu_if == 0x7} {puts "\tOFF - in the OFF state"}
|
||||
if {$pmu_if == 0x8} {puts "\tS2R_NOAOP - in the S2R_NOAOP state"}
|
||||
if {$pmu_if == 0x9} {puts "\tAOP_DDR - in the AOP_DDR state"}
|
||||
if {$pmu_if == 0xA} {puts "\tS2R_AOP - in the S2R_AOP state"}
|
||||
if {$pmu_if == 0xB} {puts "\tgoAWAKE_rst - heading from AOP_DDR to AWAKE, waiting for AWAKE_RESETN"}
|
||||
if {$pmu_if == 0xC} {puts "\tgoAWAKE_pre - preamble for AOP_DDR to AWAKE"}
|
||||
if {$pmu_if == 0xD} {puts "\tgoAWAKE_HS - handshake with miniPMGR"}
|
||||
if {$pmu_if == 0xE} {puts "\tgoAWAKE_post - postamble for AOP_DDR to AWAKE"}
|
||||
if {$pmu_if == 0xF} {puts "\tgoS2R_pre - preamble for AOP_DDR to S2R_AOP"}
|
||||
if {$pmu_if == 0x10} {puts "\tgoS2R_HS - handshake with miniPMGR"}
|
||||
if {$pmu_if == 0x11} {puts "\tgoS2R_post - postamble for AOP_DDR to S2R_AOP"}
|
||||
if {$pmu_if == 0x12} {puts "\tgoS2R_PMU - handshake with PMU"}
|
||||
if {$pmu_if == 0x13} {puts "\tgoDDR_pre - preamble for S2R_AOP to AOP_DDR"}
|
||||
if {$pmu_if == 0x14} {puts "\tgoDDR_PMU - handshake with PMU"}
|
||||
if {$pmu_if == 0x15} {puts "\tgoDDR_HS - handshake with miniPMGR"}
|
||||
if {$pmu_if == 0x16} {puts "\tgoDDR_post - postamble for S2R_AOP to AOP_DDR"}
|
||||
|
||||
#soc cfg
|
||||
set table_base [mem -memap 4 0x210000200]
|
||||
set cfg_table_address [expr $table_base | 0x210e00000]
|
||||
|
||||
set s2r_aop_to_aop_ddr [mem -memap 4 0x210000204]
|
||||
set s2r_aop_to_aop_ddr_pre_enable [expr ($s2r_aop_to_aop_ddr>>8) & 0x1]
|
||||
set s2r_aop_to_aop_ddr_pre_index [expr ($s2r_aop_to_aop_ddr>>0) & 0x1F]
|
||||
set s2r_aop_to_aop_ddr_post_enable [expr ($s2r_aop_to_aop_ddr>>24) & 0x1]
|
||||
set s2r_aop_to_aop_ddr_post_index [expr ($s2r_aop_to_aop_ddr>>16) & 0x1F]
|
||||
|
||||
set aop_ddr_to_s2r_aop [mem -memap 4 0x210000208]
|
||||
set aop_ddr_to_s2r_aop_pre_enable [expr ($aop_ddr_to_s2r_aop>>8) & 0x1]
|
||||
set aop_ddr_to_s2r_aop_pre_index [expr ($aop_ddr_to_s2r_aop>>0) & 0x1F]
|
||||
set aop_ddr_to_s2r_aop_post_enable [expr ($aop_ddr_to_s2r_aop>>24) & 0x1]
|
||||
set aop_ddr_to_s2r_aop_post_index [expr ($aop_ddr_to_s2r_aop>>16) & 0x1F]
|
||||
|
||||
set aop_ddr_to_awake [mem -memap 4 0x21000020c]
|
||||
set aop_ddr_to_awake_pre_enable [expr ($aop_ddr_to_awake>>8) & 0x1]
|
||||
set aop_ddr_to_awake_pre_index [expr ($aop_ddr_to_awake>>0) & 0x1F]
|
||||
set aop_ddr_to_awake_post_enable [expr ($aop_ddr_to_awake>>24) & 0x1]
|
||||
set aop_ddr_to_awake_post_index [expr ($aop_ddr_to_awake>>16) & 0x1F]
|
||||
|
||||
set awake_to_aop_ddr [mem -memap 4 0x210000210]
|
||||
set awake_to_aop_ddr_pre_enable [expr ($awake_to_aop_ddr>>8) & 0x1]
|
||||
set awake_to_aop_ddr_pre_index [expr ($awake_to_aop_ddr>>0) & 0x1F]
|
||||
set awake_to_aop_ddr_post_enable [expr ($awake_to_aop_ddr>>24) & 0x1]
|
||||
set awake_to_aop_ddr_post_index [expr ($awake_to_aop_ddr>>16) & 0x1F]
|
||||
|
||||
puts [format "table_base(0x210000200) 0x%x" $table_base]
|
||||
puts [format "\tConfig Table Address: 0x%x" $cfg_table_address]
|
||||
puts "Config table entries"
|
||||
md32 -memap 4 $cfg_table_address 40
|
||||
|
||||
puts [format "awake_to_aop_ddr(0x210000210) 0x%x" $awake_to_aop_ddr]
|
||||
puts [format "\tpre enable 0x%x" $awake_to_aop_ddr_pre_enable]
|
||||
puts [format "\tpre index 0x%x" $awake_to_aop_ddr_pre_index]
|
||||
puts [format "\tpost enable 0x%x" $awake_to_aop_ddr_post_enable]
|
||||
puts [format "\tpost index 0x%x" $awake_to_aop_ddr_post_index]
|
||||
puts [format "aop_ddr_to_s2r_aop(0x210000208) 0x%x" $aop_ddr_to_s2r_aop]
|
||||
puts [format "\tpre enable 0x%x" $aop_ddr_to_s2r_aop_pre_enable]
|
||||
puts [format "\tpre index 0x%x" $aop_ddr_to_s2r_aop_pre_index]
|
||||
puts [format "\tpost enable 0x%x" $aop_ddr_to_s2r_aop_post_enable]
|
||||
puts [format "\tpost index 0x%x" $aop_ddr_to_s2r_aop_post_index]
|
||||
puts [format "s2r_aop_to_aop_ddr(0x210000204) 0x%x" $s2r_aop_to_aop_ddr]
|
||||
puts [format "\tpre enable 0x%x" $s2r_aop_to_aop_ddr_pre_enable]
|
||||
puts [format "\tpre index 0x%x" $s2r_aop_to_aop_ddr_pre_index]
|
||||
puts [format "\tpost enable 0x%x" $s2r_aop_to_aop_ddr_post_enable]
|
||||
puts [format "\tpost index 0x%x" $s2r_aop_to_aop_ddr_post_index]
|
||||
puts [format "aop_ddr_to_awake(0x21000020c) 0x%x" $aop_ddr_to_awake]
|
||||
puts [format "\tpre enable 0x%x" $aop_ddr_to_awake_pre_enable]
|
||||
puts [format "\tpre index 0x%x" $aop_ddr_to_awake_pre_index]
|
||||
puts [format "\tpost enable 0x%x" $aop_ddr_to_awake_post_enable]
|
||||
puts [format "\tpost index 0x%x" $aop_ddr_to_awake_post_index]
|
||||
|
||||
puts "Trace Entry"
|
||||
mem -memap 4 0x210E9E500
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/python
|
||||
import argparse
|
||||
import collections
|
||||
import pipes
|
||||
import sys
|
||||
|
||||
WRITE_COMMAND = 0
|
||||
WRITE_COMPRESSED_COMMAND = 1
|
||||
READ_COMMAND = 2
|
||||
WRITE64_COMMAND = 3
|
||||
DELAY_COMMAND = 4
|
||||
|
||||
def find_string(line, word):
|
||||
t = line.split()
|
||||
for x in t:
|
||||
if word in x:
|
||||
s = x.find('=')
|
||||
addr = x[s+1:]
|
||||
return addr
|
||||
|
||||
def convert_seqto_struct(filename, sequence):
|
||||
a = []
|
||||
with open (filename, 'rb') as config_seq_file:
|
||||
for line in config_seq_file:
|
||||
if sequence in line and 'comment' not in line:
|
||||
for line in config_seq_file:
|
||||
if 'cfg_end' in line:
|
||||
break
|
||||
elif 'comment' in line or 'write_start' in line or 'write_end' in line:
|
||||
continue
|
||||
elif 'poll' in line:
|
||||
# poll command
|
||||
addr = find_string(line, 'reg_addr')
|
||||
value = find_string(line, 'reg_value')
|
||||
mask = find_string(line, 'reg_mask')
|
||||
retryEnable = find_string(line, 'retry_en')
|
||||
retryCnt = find_string(line, 'retry_cnt')
|
||||
a.append((addr, value, READ_COMMAND, mask, retryEnable, retryCnt))
|
||||
elif 'write64' in line:
|
||||
# 64 BIT write commands
|
||||
addr = find_string(line, 'reg_addr')
|
||||
value = find_string(line, 'reg_value')
|
||||
a.append((addr, value, WRITE64_COMMAND))
|
||||
elif 'cfg_cnt' in line:
|
||||
value = line.split()[1]
|
||||
a.append((0, value, DELAY_COMMAND))
|
||||
else:
|
||||
# write commands
|
||||
addr = find_string(line, 'reg_addr')
|
||||
value = find_string(line, 'reg_value')
|
||||
a.append((addr, value, WRITE_COMMAND))
|
||||
|
||||
|
||||
j = 0
|
||||
print 'uint32_t %s[] = {' % (sequence.split()[1])
|
||||
for i in xrange(len(a)):
|
||||
if WRITE_COMMAND == a[i][2]:
|
||||
wrCommand = ((int(a[i][0], 16)>>10)<<6) | (1)
|
||||
offCommand = (int(a[i][0], 16)>>2) & 0xff
|
||||
wrDword = int(a[i][1], 16)
|
||||
print '\t0x{:X}, 0x{:X}, 0x{:X},\t\t\t\t// Write'.format(wrCommand, offCommand, wrDword)
|
||||
j += 3
|
||||
elif READ_COMMAND == a[i][2]:
|
||||
rdCommand = ((int(a[i][0], 16)>>10)<<6) | (2)
|
||||
offCommand = (int(a[i][4], 16)<<16) | ((int(a[i][5], 16) & 0xFF)<<8) | ((int(a[i][0], 16)>>2) & 0xFF)
|
||||
rdDataMask = int(a[i][3], 16)
|
||||
rdDword = int(a[i][1], 16)
|
||||
print '\t0x{:X}, 0x{:X}, 0x{:X}, 0x{:X},\t\t// Read'.format(rdCommand, offCommand, rdDataMask, rdDword)
|
||||
j += 4
|
||||
elif WRITE64_COMMAND == a[i][2]:
|
||||
wrCommand = ((int(a[i][0], 16)>>10)<<6) | (1<<1) | (1)
|
||||
offCommand = (int(a[i][0], 16)>>2) & 0xff
|
||||
wrDwordLow = int(a[i][1], 16) & 0xFFFFFFFF
|
||||
wrDwordHigh = (int(a[i][1], 16) >> 32) & 0xFFFFFFFF
|
||||
if ((j+2) % 8) :
|
||||
print '\t0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X},\t\t\t\t// Write'.format(wrCommand, offCommand, 0x0, wrDwordLow, wrDwordHigh)
|
||||
j += 5
|
||||
else:
|
||||
print '\t0x{:X}, 0x{:X}, 0x{:X}, 0x{:X},\t\t\t\t// Write'.format(wrCommand, offCommand, wrDwordLow, wrDwordHigh)
|
||||
j += 4
|
||||
elif DELAY_COMMAND == a[i][2]:
|
||||
delayCommand = (int(a[i][1], 16)<<6) | 4
|
||||
print '\t0x{:X}, \t\t// Delay'.format(delayCommand)
|
||||
j += 1
|
||||
|
||||
|
||||
print '};'
|
||||
|
||||
def main():
|
||||
argparser = argparse.ArgumentParser(description='Converts config sequence to data structures to to be programmed')
|
||||
argparser.add_argument('filename')
|
||||
args = argparser.parse_args()
|
||||
|
||||
convert_seqto_struct(args.filename, 'seq_name: awake_to_aop_ddr_pre')
|
||||
convert_seqto_struct(args.filename, 'seq_name: awake_to_aop_ddr_post')
|
||||
convert_seqto_struct(args.filename, 'seq_name: aop_ddr_to_s2r_aop_pre')
|
||||
convert_seqto_struct(args.filename, 'seq_name: s2r_aop_to_aop_ddr_post')
|
||||
convert_seqto_struct(args.filename, 'seq_name: aop_ddr_to_awake_pre')
|
||||
convert_seqto_struct(args.filename, 'seq_name: aop_ddr_to_awake_post')
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,988 @@
|
||||
#!/usr/bin/python
|
||||
import argparse
|
||||
import collections
|
||||
import csv
|
||||
import pipes
|
||||
import sys
|
||||
|
||||
try:
|
||||
import openpyxl
|
||||
except ImportError:
|
||||
openpyxl = None
|
||||
|
||||
class ParseError(Exception): pass
|
||||
|
||||
'''
|
||||
Representation of one pin in the GPIO config
|
||||
'''
|
||||
class BasePin(object):
|
||||
default_drive = 'X1'
|
||||
valid_pupdn = set(["PULL_UP", "PULL_DOWN", ""])
|
||||
valid_drive_strength = set()
|
||||
def __init__(self, gpio_pin_number):
|
||||
self.gpio_pin_number = int(gpio_pin_number)
|
||||
self.bump_name = None
|
||||
self.net_name = None
|
||||
self.function = None
|
||||
self.drive_strength = 'X1'
|
||||
self.pupd = None
|
||||
self.bus_hold = None
|
||||
self.input_mode = None
|
||||
self.row_number = None
|
||||
self.slew_rate = ''
|
||||
self.rom = None
|
||||
|
||||
@property
|
||||
def non_empty_props(self):
|
||||
if self.drive_strength == self.default_drive:
|
||||
drive_strength = ''
|
||||
else:
|
||||
drive_strength = "DRIVE_{}".format(self.drive_strength)
|
||||
|
||||
if self.slew_rate == '':
|
||||
slew_rate = ''
|
||||
else:
|
||||
slew_rate = "{}_SLEW".format(self.slew_rate)
|
||||
|
||||
return [i for i in [self.function, self.pupd, drive_strength, slew_rate, self.bus_hold, self.input_mode] if i]
|
||||
|
||||
def to_enum_key(self):
|
||||
return 'GPIOCFG_' + '_'.join(self.non_empty_props)
|
||||
|
||||
def to_enum_value(self):
|
||||
return ' | '.join(self.non_empty_props)
|
||||
|
||||
def to_pinconfig_line(self, use_enum):
|
||||
if None in (self.bump_name, self.net_name, self.function, self.drive_strength, self.pupd):
|
||||
raise ParseError, "Some attributes were unset for pin %d: %s" % (self.gpio_pin_number, (self.bump_name, self.net_name, self.function, self.drive_strength, self.pupd))
|
||||
|
||||
if use_enum:
|
||||
args = '{},'.format(self.to_enum_key())
|
||||
else:
|
||||
args = '{},'.format(self.to_enum_value())
|
||||
|
||||
gpio_comment = '// {:>3} : {}'.format(self.gpio_pin_number, self.bump_name)
|
||||
net_name_comment = '-> {}'.format(self.net_name)
|
||||
return [args, gpio_comment, net_name_comment]
|
||||
|
||||
def set_function(self, fn):
|
||||
cfg_map = { 'in': 'CFG_IN',
|
||||
'input': 'CFG_IN',
|
||||
'out0': 'CFG_OUT_0',
|
||||
'out 0': 'CFG_OUT_0',
|
||||
'out1': 'CFG_OUT_1',
|
||||
'out 1': 'CFG_OUT_1',
|
||||
'alt func 0': 'CFG_FUNC0',
|
||||
'alt func 1': 'CFG_FUNC1',
|
||||
'alt func 2': 'CFG_FUNC2',
|
||||
'int rising': 'CFG_IN',
|
||||
'int falling': 'CFG_IN',
|
||||
'int any': 'CFG_IN',
|
||||
'disable': 'CFG_DISABLED',
|
||||
'disabled': 'CFG_DISABLED',
|
||||
# The numbering offset isn't a typo, these are legacy
|
||||
'function 1': 'CFG_FUNC0',
|
||||
'function 2': 'CFG_FUNC1',
|
||||
'function 3': 'CFG_FUNC2',
|
||||
}
|
||||
self.function = cfg_map[fn.lower()]
|
||||
|
||||
def set_net_name(self, name):
|
||||
self.net_name = name.strip()
|
||||
|
||||
def set_bump_name(self, name):
|
||||
self.bump_name = name.strip()
|
||||
|
||||
def set_drive_strength(self, strength):
|
||||
if strength == '':
|
||||
strength = self.default_drive
|
||||
else:
|
||||
strength = strength.upper()
|
||||
|
||||
if strength.isdigit():
|
||||
strength = self.drive_strength_prefix + strength
|
||||
|
||||
if not strength in self.valid_drive_strength:
|
||||
sorted_drive_strengths = sorted(self.valid_drive_strength, key=lambda item: (int(item[1:])))
|
||||
raise ParseError, "drive strength '{}' not valid (options are {})".format(strength.upper(), sorted_drive_strengths)
|
||||
self.drive_strength = strength
|
||||
|
||||
def set_pupd(self, pupd):
|
||||
pupd_map = { 'pullup strong' : 'PULL_UP_STRONG',
|
||||
'spu' : 'PULL_UP_STRONG',
|
||||
'pullup': 'PULL_UP',
|
||||
'pu': 'PULL_UP',
|
||||
'pulldown': 'PULL_DOWN',
|
||||
'pd': 'PULL_DOWN',
|
||||
'disable': '',
|
||||
'': ''}
|
||||
if not pupd_map[pupd.lower()] in self.valid_pupdn:
|
||||
pretty_pupdn = filter(lambda x : pupd_map[x] in self.valid_pupdn , pupd_map.keys())
|
||||
raise ParseError, "pu/pd value '{}' not valid (options are {})".format(pupd, pretty_pupdn)
|
||||
self.pupd = pupd_map[pupd.lower()]
|
||||
|
||||
|
||||
class PinV1(BasePin):
|
||||
valid_drive_strength = set(["X1", "X2", "X3", "X4"])
|
||||
drive_strength_prefix = 'X'
|
||||
|
||||
def set_bus_hold(self, hold):
|
||||
bus_hold_map = { '': '', '0': '', None: '', 'y': 'BUS_HOLD', 'yes': 'BUS_HOLD', '1': 'BUS_HOLD'}
|
||||
self.bus_hold = bus_hold_map[hold.lower()]
|
||||
|
||||
class PinV2(BasePin):
|
||||
valid_drive_strength = set(["X1", "X2", "X4", "X6"])
|
||||
drive_strength_prefix = 'X'
|
||||
|
||||
def set_bus_hold(self, hold):
|
||||
bus_hold_map = { '': '', '0': '', None: '', 'y': 'BUS_HOLD', 'yes': 'BUS_HOLD', '1': 'BUS_HOLD'}
|
||||
self.bus_hold = bus_hold_map[hold.lower()]
|
||||
|
||||
class PinV3(BasePin):
|
||||
valid_drive_strength = set(["X1", "X2", "X4", "X6"])
|
||||
drive_strength_prefix = 'X'
|
||||
|
||||
def set_slew_rate(self, slew):
|
||||
slew_map = { '': '', None: '', "fast": "FAST", "slow": "SLOW" }
|
||||
self.slew_rate = slew_map[slew.lower()]
|
||||
|
||||
class PinV4(PinV3):
|
||||
def set_input_mode(self, mode):
|
||||
mode_map = { '': '', None: '', 'cmos': '', 'schmitt': 'INPUT_SCHMITT', 'Schmitt': 'INPUT_SCHMITT'}
|
||||
self.input_mode = mode_map[mode]
|
||||
|
||||
class PinV5(PinV4):
|
||||
default_drive = 'S0'
|
||||
valid_drive_strength = set(["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10", "S11", "S12", "S13", "S14", "S15"])
|
||||
drive_strength_prefix = 'S'
|
||||
valid_pupdn = set(["PULL_UP_STRONG", "PULL_UP", "PULL_DOWN", ""])
|
||||
|
||||
def set_slew_rate(self, slew):
|
||||
slew_map = { '': '', None: '', "fast": "FAST", "slow": "SLOW", "very fast": "VERY_FAST" }
|
||||
self.slew_rate = slew_map[slew.lower()]
|
||||
|
||||
|
||||
class ReservedPin(object):
|
||||
def to_pinconfig_line(self, use_enum):
|
||||
return ['{},'.format(self.to_enum_key() if use_enum else self.to_enum_value())]
|
||||
|
||||
def to_enum_key(self):
|
||||
return 'GPIOCFG_CFG_DISABLED'
|
||||
|
||||
def to_enum_value(self):
|
||||
return 'CFG_DISABLED'
|
||||
|
||||
class GPIODomainInfo(object):
|
||||
def __init__(self, offset, count, reserved):
|
||||
self.id_offset = offset
|
||||
self.pin_count = count
|
||||
self.reserved_pins = reserved
|
||||
|
||||
class GPIODomain(object):
|
||||
def __init__(self, info, pin_class):
|
||||
self.id_offset = info.id_offset
|
||||
self.pin_count = info.pin_count
|
||||
self.reserved_pins = info.reserved_pins
|
||||
|
||||
# GPIO domains must start and end on 8-pin boundaries
|
||||
assert self.id_offset % 8 == 0
|
||||
if self.pin_count % 8 != 0:
|
||||
new_pin_count = self.pin_count + 8 - self.pin_count % 8
|
||||
self.reserved_pins |= set(range(self.id_offset + self.pin_count, self.id_offset + new_pin_count))
|
||||
self.pin_count = new_pin_count
|
||||
|
||||
self.pins = [None] * self.pin_count
|
||||
|
||||
for i in range(self.pin_count):
|
||||
if not i + self.id_offset in self.reserved_pins:
|
||||
pin = pin_class(i)
|
||||
pin.set_bump_name("UNSPECIFIED")
|
||||
pin.set_net_name("UNSPECIFIED")
|
||||
pin.set_function("disable")
|
||||
pin.set_pupd("disable")
|
||||
pin.set_drive_strength('')
|
||||
else:
|
||||
pin = ReservedPin()
|
||||
self.pins[i] = pin
|
||||
|
||||
def has_pin_id(self, pin_id):
|
||||
return pin_id >= self.id_offset and pin_id < self.id_offset + self.pin_count
|
||||
|
||||
def get_pin(self, pin_id):
|
||||
if not self.has_pin_id(pin_id):
|
||||
raise IndexError
|
||||
return self.pins[pin_id - self.id_offset]
|
||||
|
||||
class BasePinConfig(object):
|
||||
# Should be overridden in subclasses to a subclass of BasePin
|
||||
pin_class = None
|
||||
domain_info = None
|
||||
|
||||
def __init__(self):
|
||||
self.domains = [GPIODomain(info, self.pin_class) for info in self.domain_info]
|
||||
|
||||
def get_pin(self, pin_id):
|
||||
for d in self.domains:
|
||||
if d.has_pin_id(pin_id):
|
||||
return d.get_pin(pin_id)
|
||||
raise IndexError
|
||||
|
||||
def array_size(self, domain_index):
|
||||
if domain_index == 0:
|
||||
count_label = ''
|
||||
else:
|
||||
count_label = '_{}'.format(domain_index)
|
||||
return 'GPIO{}_GROUP_COUNT * GPIOPADPINS'.format(count_label)
|
||||
|
||||
def array_declaration(self, array_type, name, domain_index):
|
||||
if domain_index == 0:
|
||||
count_label = ''
|
||||
else:
|
||||
count_label = '_{}'.format(domain_index)
|
||||
templ = 'static const {} {}_{}[{}] = {{'
|
||||
return templ.format(array_type, name, domain_index, self.array_size(domain_index))
|
||||
|
||||
def to_size_array(self, max_domains):
|
||||
lines = []
|
||||
lines += ['static const uint32_t controller_pins[GPIOC_COUNT] = {']
|
||||
for domain_idx in range(min(len(self.domains), max_domains)):
|
||||
lines += ['\t{},'.format(self.array_size(domain_idx))]
|
||||
lines += ['};']
|
||||
return '\n'.join(lines)
|
||||
|
||||
def to_pinconfig_struct(self, name, array_type, max_domains, use_enum):
|
||||
lines = []
|
||||
|
||||
for domain_idx in range(min(len(self.domains), max_domains)):
|
||||
domain = self.domains[domain_idx]
|
||||
|
||||
pin_matrix = [domain.pins[i].to_pinconfig_line(use_enum) for i in range(domain.pin_count)]
|
||||
pin_matrix = tab_align(pin_matrix, min_column_tabs={0: 8})
|
||||
|
||||
lines.append(self.array_declaration(array_type, name, domain_idx))
|
||||
|
||||
rownum = 0
|
||||
for row in pin_matrix:
|
||||
if (rownum % 8) == 0:
|
||||
lines.append("\n/* Port {:2} */".format(rownum/8))
|
||||
lines.append("\t"+"".join(row))
|
||||
rownum += 1
|
||||
lines.append("};")
|
||||
lines.append('')
|
||||
return "\n".join(lines)
|
||||
|
||||
def to_enum_items(self):
|
||||
items = set()
|
||||
|
||||
for domain in self.domains:
|
||||
for pin in domain.pins:
|
||||
items.add((pin.to_enum_key(), pin.to_enum_value()))
|
||||
return items
|
||||
|
||||
class BasePinConfigV1(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV1
|
||||
|
||||
class BasePinConfigV2(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV2
|
||||
|
||||
class BasePinConfigV3(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV3
|
||||
|
||||
class BasePinConfigV4(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV4
|
||||
|
||||
class BasePinConfigV5(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV5
|
||||
|
||||
class H4APinConfig(BasePinConfigV1):
|
||||
domain_info = [GPIODomainInfo(offset=0, count=208, reserved=set())]
|
||||
|
||||
class H5PPinConfig(BasePinConfigV2):
|
||||
domain_info = [GPIODomainInfo(offset=0, count=232, reserved=set())]
|
||||
|
||||
class H5GPinConfig(BasePinConfigV2):
|
||||
reserved_pins = set(range(14, 32) + range(140, 160) + range(212, 224) + range(241, 248))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=248, reserved=reserved_pins)]
|
||||
|
||||
class AlcatrazPinConfig(BasePinConfigV3):
|
||||
reserved_pins = set(range(24, 32) + range(45, 64) + range(151, 160) + range(173, 192))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=195, reserved=reserved_pins)]
|
||||
|
||||
class FijiPinConfig(BasePinConfigV4):
|
||||
reserved_pins = set(range(16, 32) + range(88, 96) + range(140, 160))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=204, reserved=reserved_pins)]
|
||||
|
||||
class CapriPinConfig(BasePinConfigV4):
|
||||
reserved_pins = set(range(16, 32))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=184, reserved=reserved_pins)]
|
||||
|
||||
class M7PinConfig(BasePinConfigV4):
|
||||
reserved_pins = set(range(10, 32))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=138, reserved=reserved_pins),
|
||||
GPIODomainInfo(offset=160, count=50, reserved=set())]
|
||||
|
||||
class MauiPinConfig(BasePinConfigV4):
|
||||
reserved_pins = set(range(55, 64) + range(92, 96) + range(152, 160) + range(168, 192))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=199, reserved=reserved_pins),
|
||||
GPIODomainInfo(offset=288, count=28, reserved=set())]
|
||||
|
||||
class ElbaPinConfig(BasePinConfigV5):
|
||||
reserved_pins = set(range(37, 64) + range(125, 128) + range(142, 160))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=220, reserved=reserved_pins),
|
||||
GPIODomainInfo(offset=288, count=28, reserved=set())]
|
||||
|
||||
class CaymanPinConfig(BasePinConfigV5):
|
||||
ap_reserved_pins = set(range(55, 64) + range(92, 96) + range(104, 128) + range(155, 160) + range(188, 192))
|
||||
aop_reserved_pins = set(range(318, 320) + range(330, 352))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=208, reserved=ap_reserved_pins), # AP
|
||||
GPIODomainInfo(offset=288, count=85, reserved=aop_reserved_pins), # AOP
|
||||
GPIODomainInfo(offset=256, count=7, reserved=set())] # SEP
|
||||
output_domains = 2
|
||||
|
||||
class M8PinConfig(BasePinConfigV5):
|
||||
ap_reserved_pins = set(range(138, 160))
|
||||
aop_reserved_pins = set(range(264, 288))
|
||||
domain_info = [GPIODomainInfo(offset=32, count=139, reserved=ap_reserved_pins), # AP
|
||||
GPIODomainInfo(offset=256, count=96, reserved=aop_reserved_pins), # AOP
|
||||
GPIODomainInfo(offset=0, count=7, reserved=set()),
|
||||
GPIODomainInfo(offset=192, count=96, reserved=set())]
|
||||
output_domains = 2
|
||||
|
||||
def excel_reader(filename, sheet):
|
||||
try:
|
||||
workbook = openpyxl.load_workbook(filename, data_only=True)
|
||||
except IOError:
|
||||
sys.stderr.write('Could not open workbook {}\n'.format(filename))
|
||||
return None
|
||||
try:
|
||||
worksheet = workbook.get_sheet_by_name(sheet)
|
||||
except KeyError:
|
||||
sys.stderr.write('Could not open worksheet {}\n'.format(sheet))
|
||||
return None
|
||||
|
||||
def row_generator(ws):
|
||||
def cell_to_str(cell):
|
||||
value = cell.value
|
||||
if value is None:
|
||||
value = ''
|
||||
return unicode(value)
|
||||
|
||||
for row in ws.rows:
|
||||
yield [cell_to_str(cell) for cell in row]
|
||||
|
||||
return row_generator(worksheet)
|
||||
|
||||
def try_parse_hex(i):
|
||||
try:
|
||||
return int(i, 16)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def row_is_empty(row):
|
||||
for col in row:
|
||||
if not col is None and len(col.strip()) > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def row_is_footer(row):
|
||||
for col in row:
|
||||
if not col is None:
|
||||
stripped = col.strip()
|
||||
if len(stripped) > 0 and stripped != '--':
|
||||
return False
|
||||
return True
|
||||
|
||||
class CSVParser(object):
|
||||
def __init__(self, pinconfig_class, per_target_headers):
|
||||
self.pinconfig_class = pinconfig_class
|
||||
self.per_target_headers = per_target_headers
|
||||
self.targets = per_target_headers.keys()
|
||||
# Map the standardized name for a property to a list of acceptable names
|
||||
self.header_name_map = { 'gpio_id': ['gpio id', 'id'],
|
||||
'net_name': ['net name', 'net names'],
|
||||
'bump_name': ['ball name', 'pad name'],
|
||||
'configurable': ['gpio configurable'],
|
||||
'drive_strength': ['drive strength', 'drive strength (ma)'],
|
||||
'drive_strength_ap': ['drive strength form factor', 'drive strength ap'],
|
||||
'drive_strength_dev': ['drive strength dev'],
|
||||
'pupd': ['pu/pd', 'pullup/pulldown', 'pull-up/pull-down'],
|
||||
'function': ['config', 'dir', 'SecureROM config'],
|
||||
'rom': ['rom function']}
|
||||
|
||||
if callable(getattr(self.pinconfig_class.pin_class, 'set_slew_rate', None)):
|
||||
self.header_name_map['slew_rate'] = ['slew rate', 'gpio_sr']
|
||||
self.header_name_map['slew_rate_ap'] = ['slew rate form factor', 'gpio_sr ap']
|
||||
self.header_name_map['slew_rate_dev'] = ['slew rate dev', 'gpio_sr dev']
|
||||
|
||||
if callable(getattr(self.pinconfig_class.pin_class, 'set_bus_hold', None)):
|
||||
self.header_name_map['bus_hold'] = ['bus hold', 'gpio_bh']
|
||||
|
||||
if callable(getattr(self.pinconfig_class.pin_class, 'set_input_mode', None)):
|
||||
self.header_name_map['input_mode'] = ['input mode', 'input select', 'gpio_is']
|
||||
|
||||
def create_pinconfig(self):
|
||||
return self.pinconfig_class()
|
||||
|
||||
def parse_version(self, row):
|
||||
for col in row:
|
||||
lowercase_col = col.lower()
|
||||
if 'rev' in lowercase_col or 'version' in lowercase_col or 'ver' in lowercase_col:
|
||||
return lowercase_col
|
||||
return None
|
||||
|
||||
def canonical_header(self, header):
|
||||
for canonical, options in self.header_name_map.iteritems():
|
||||
if header.lower() in options:
|
||||
return canonical
|
||||
return None
|
||||
|
||||
def find_headers(self, row, rom):
|
||||
lowercase_row = map(lambda x: x.lower(), row)
|
||||
columns_per_target = {}
|
||||
columns_per_config = collections.defaultdict(dict)
|
||||
per_config_columns = set()
|
||||
ignore_missing_keys = set()
|
||||
missing_keys = set()
|
||||
|
||||
# create a mapping from canonical header names to column indices
|
||||
header_map = {}
|
||||
for idx in range(len(lowercase_row)):
|
||||
header = lowercase_row[idx]
|
||||
canonical = self.canonical_header(header)
|
||||
if canonical is None:
|
||||
continue
|
||||
header_map[canonical] = idx
|
||||
|
||||
# some parameters can be specified per config (ap/dev). figure out
|
||||
# which if any are done that way for this spreadsheet
|
||||
config_names = ['ap', 'dev']
|
||||
for key in ['drive_strength', 'slew_rate', 'net_name']:
|
||||
config_to_key_map = {x: '{}_{}'.format(key, x) for x in config_names}
|
||||
per_config_keys = set(config_to_key_map.values())
|
||||
|
||||
if per_config_keys <= set(header_map):
|
||||
assert True, 'error message' # XXX
|
||||
# used in the loop below to skip over columns that are per config
|
||||
per_config_columns.update(per_config_keys)
|
||||
# passed back to the caller for use in parsing rows
|
||||
for config in config_names:
|
||||
columns_per_config[config][key] = header_map[config_to_key_map[config]]
|
||||
ignore_missing_keys.add(key)
|
||||
elif not per_config_keys.isdisjoint(set(header_map)):
|
||||
# either all or none of per-config columns should be present
|
||||
found = ', '.join(sorted(set(header_map) & per_config_keys))
|
||||
raise ParseError('All or none of the per-config {} columns must be specified (found {})'.format(key, found))
|
||||
else:
|
||||
# this key isn't per config, so fall through to the logic below, but
|
||||
# let the missing column checker know the per-config columns won't be present
|
||||
ignore_missing_keys.update(per_config_keys)
|
||||
|
||||
# If no per-config columns were set, then there's only one configuration.
|
||||
# Set up the key map accordingly
|
||||
if len(columns_per_config) == 0:
|
||||
for key in ['drive_strength', 'slew_rate']:
|
||||
columns_per_config['all'][key] = header_map[key]
|
||||
|
||||
for target in self.targets:
|
||||
target_header_name_map = self.per_target_headers[target]
|
||||
columns_per_target[target] = {}
|
||||
for key in self.header_name_map.keys():
|
||||
if key in per_config_columns:
|
||||
continue
|
||||
if key in target_header_name_map:
|
||||
header = target_header_name_map[key]
|
||||
# This value has a column per target, so find the per-target header in the row
|
||||
try:
|
||||
idx = lowercase_row.index(header.lower())
|
||||
except ValueError:
|
||||
sys.stderr.write('Could not find target {}\'s "{}" column "{}"\n'.format(target, key, header))
|
||||
missing_keys.add(key)
|
||||
columns_per_target[target][key] = idx
|
||||
else:
|
||||
# This value has one column for all targets, so find one of the standard headers in the row
|
||||
for header in self.header_name_map[key]:
|
||||
if header.lower() in lowercase_row:
|
||||
idx = lowercase_row.index(header.lower())
|
||||
columns_per_target[target][key] = idx
|
||||
break
|
||||
else:
|
||||
missing_keys.add(key)
|
||||
|
||||
if rom:
|
||||
for key, value in target_header_name_map.iteritems():
|
||||
if key == 'function' and value.lower() != 'SecureROM Config'.lower():
|
||||
sys.stderr.write('ROM pins should have SecureROM Config column defined\n')
|
||||
missing_keys.add(key)
|
||||
|
||||
# Some columns are optional
|
||||
missing_keys -= set(['configurable', 'input_mode'])
|
||||
# ROM Function is optional if not --rom
|
||||
if not rom:
|
||||
missing_keys -= set(['rom'])
|
||||
# Marked as being unneeded by the per-config column logic
|
||||
missing_keys -= ignore_missing_keys
|
||||
|
||||
if len(missing_keys) == 0:
|
||||
missing_keys = None
|
||||
|
||||
return columns_per_target, columns_per_config, missing_keys
|
||||
|
||||
def parse(self, filename, force=False, rom=False, sheet=None):
|
||||
if filename.endswith('.xlsx'):
|
||||
if openpyxl is None:
|
||||
sys.stderr.write('To import Excel sheets, install the openpyxl package\n')
|
||||
return None, None
|
||||
if sheet is None:
|
||||
sys.stderr.write('To import Excel sheets, specify the --sheet option\n')
|
||||
return None, None
|
||||
reader = excel_reader(filename, sheet)
|
||||
if reader is None:
|
||||
return None, None
|
||||
else:
|
||||
reader = csv.reader(open(filename, "rU"))
|
||||
cnt=0
|
||||
row_number = 0
|
||||
|
||||
# top row has version number
|
||||
row0 = reader.next()
|
||||
row_number += 1
|
||||
# next non-blank row has headers
|
||||
while True:
|
||||
row1 = reader.next()
|
||||
row_number += 1
|
||||
if len(filter(lambda x: len(x.strip()) > 0, row1)) > 0:
|
||||
break
|
||||
|
||||
version = self.parse_version(row0)
|
||||
if version is None:
|
||||
sys.stderr.write('Could not find version in 1st row\n')
|
||||
return None, None
|
||||
|
||||
try:
|
||||
columns_per_target, columns_per_config, missing = self.find_headers(row1, rom)
|
||||
except ParseError as e:
|
||||
sys.stderr.write('{}\n'.format(e))
|
||||
return None, None
|
||||
|
||||
if not missing is None:
|
||||
missing_options = {"{} ({})".format(col, " or ".join(self.header_name_map[col])) for col in missing}
|
||||
sys.stderr.write('Missing columns:\n{}\n'.format('\n '.join(missing_options)))
|
||||
return None, None
|
||||
|
||||
if len(columns_per_config) == 1:
|
||||
configs = ['all']
|
||||
else:
|
||||
configs = ['ap', 'dev']
|
||||
|
||||
pinconfigs = {}
|
||||
for target in self.targets:
|
||||
pinconfigs[target] = {}
|
||||
for config in configs:
|
||||
pinconfigs[target][config] = self.create_pinconfig()
|
||||
|
||||
have_error = False
|
||||
|
||||
for row in reader:
|
||||
row_number += 1
|
||||
if row_is_empty(row):
|
||||
continue
|
||||
if row_is_footer(row):
|
||||
break
|
||||
|
||||
for target, target_columns in columns_per_target.iteritems():
|
||||
for config, config_columns in columns_per_config.iteritems():
|
||||
columns = {}
|
||||
columns.update(config_columns)
|
||||
columns.update(target_columns)
|
||||
|
||||
try:
|
||||
parsed_props = {prop: row[column].strip() for prop, column in columns.iteritems()}
|
||||
|
||||
# If the optional 'GPIO Configurable' column is present, make sure it says y or yes
|
||||
try:
|
||||
if not parsed_props['configurable'].lower() in ('y', 'yes'):
|
||||
continue
|
||||
# And then get rid of the column, because the code below can't do anything with it
|
||||
del parsed_props['configurable']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
gpio_id = int(parsed_props['gpio_id'])
|
||||
except ValueError:
|
||||
if rom and parsed_props['gpio_id'] == '':
|
||||
continue
|
||||
else:
|
||||
raise ParseError('Invalid GPIO ID "{}"'.format(parsed_props['gpio_id']))
|
||||
|
||||
pinconfig = pinconfigs[target][config]
|
||||
try:
|
||||
pin = pinconfig.get_pin(gpio_id)
|
||||
except IndexError:
|
||||
raise ParseError('GPIO ID {} is out of range'.format(gpio_id))
|
||||
|
||||
if isinstance(pin, ReservedPin):
|
||||
raise ParseError('GPIO ID {} is reserved pin'.format(gpio_id))
|
||||
elif not pin.row_number is None:
|
||||
raise ParseError('GPIO ID {} redefined (previously defined on row {})'.format(pin.gpio_id, pin.row_number))
|
||||
pin.row_number = row_number
|
||||
|
||||
# The setters for all of the columns are predictable based on their name
|
||||
for prop, value in parsed_props.iteritems():
|
||||
if rom:
|
||||
if prop == 'function':
|
||||
if parsed_props['rom'].lower() in ('y', 'yes'):
|
||||
value = parsed_props['function']
|
||||
else:
|
||||
value = 'disabled'
|
||||
if prop == 'net_name':
|
||||
if not parsed_props['rom'].lower() in ('y', 'yes'):
|
||||
value = ''
|
||||
if prop != 'gpio_id' and prop != 'rom':
|
||||
setter = getattr(pin, 'set_' + prop)
|
||||
try:
|
||||
setter(value)
|
||||
except KeyError:
|
||||
raise ParseError('Invalid value "{}" for key {} in column "{}"'.format(value, prop, row1[columns[prop]]))
|
||||
except ParseError as e:
|
||||
sys.stderr.write('Row {:3}, Config {:3}: {}\n'.format(row_number, config, e))
|
||||
have_error = True
|
||||
|
||||
if not have_error or force:
|
||||
return version, pinconfigs
|
||||
else:
|
||||
return None, None
|
||||
|
||||
def tab_align(data_matrix, tabwidth=8, min_spacing_slack=1, min_column_tabs={}):
|
||||
def rounduptab(x):
|
||||
return int(((x + float(tabwidth-1))/tabwidth))*tabwidth
|
||||
def tab(x):
|
||||
if x % tabwidth:
|
||||
return rounduptab(x)
|
||||
else:
|
||||
return x+tabwidth
|
||||
num_cols = max(map(len, data_matrix))
|
||||
|
||||
# Compute max number of characters seen for each column
|
||||
max_char_counts = [0]*num_cols
|
||||
for column, min_width in min_column_tabs.iteritems():
|
||||
max_char_counts[column] = min_width*tabwidth
|
||||
def grow_count(new_row):
|
||||
for i in range(len(new_row)):
|
||||
max_char_counts[i] = max(max_char_counts[i], rounduptab(len(new_row[i]) + min_spacing_slack))
|
||||
for row in data_matrix:
|
||||
grow_count(row)
|
||||
|
||||
def pad_tabs(item, required_length):
|
||||
length = len(item)
|
||||
tabcount = 0
|
||||
while length < required_length:
|
||||
tabcount+=1
|
||||
length=tab(length)
|
||||
return "%s%s" % (item, '\t'*tabcount)
|
||||
def format_row(row):
|
||||
return "".join([pad_tabs(a, b) for (a,b) in zip(row, max_char_counts)]).rstrip()
|
||||
return [ format_row(x) for x in data_matrix ]
|
||||
|
||||
def print_pinconfigs(target_config_pinconfigs, pinconfig_class, version, radar, copyright, commandline, prefix, rom, header):
|
||||
array_type = pinconfig_class.array_type
|
||||
num_domains = len(pinconfig_class.domain_info)
|
||||
if hasattr(pinconfig_class, 'output_domains'):
|
||||
if pinconfig_class.output_domains < num_domains:
|
||||
num_domains = pinconfig_class.output_domains
|
||||
|
||||
targets = sorted(target_config_pinconfigs.keys())
|
||||
configs = sorted(target_config_pinconfigs[targets[0]].keys())
|
||||
|
||||
if header:
|
||||
pinconfig_prefix = 'gpio_default_cfg'
|
||||
else:
|
||||
pinconfig_prefix = 'pinconfig'
|
||||
|
||||
if not prefix is None:
|
||||
pinconfig_prefix = "{}_{}".format(pinconfig_prefix, prefix)
|
||||
|
||||
if rom:
|
||||
assert header
|
||||
assert configs == ['all']
|
||||
assert len(targets) == 1
|
||||
assert len(configs) == 1
|
||||
else:
|
||||
assert configs == ['ap', 'dev'] or configs == ['all']
|
||||
|
||||
print "/*"
|
||||
print " * Copyright (C) {} Apple Inc. All rights reserved.".format(copyright)
|
||||
print " *"
|
||||
print " * This document is the property of Apple Inc."
|
||||
print " * It is considered confidential and proprietary."
|
||||
print " *"
|
||||
print " * This document may not be reproduced or transmitted in any form,"
|
||||
print " * in whole or in part, without the express written permission of"
|
||||
print " * Apple Inc."
|
||||
print " */"
|
||||
print ""
|
||||
print "/* THIS FILE IS AUTOMATICALLY GENERATED BY tools/csvtopinconfig.py. DO NOT EDIT!"
|
||||
print " I/O Spreadsheet version: {}".format(version)
|
||||
print " I/O Spreadsheet tracker: {}".format(radar)
|
||||
print " Conversion command: {}".format(commandline)
|
||||
print "*/"
|
||||
print ""
|
||||
|
||||
if header:
|
||||
print "#ifndef __PLATFORM_PINCONFIG_H"
|
||||
print "#define __PLATFORM_PINCONFIG_H"
|
||||
print ""
|
||||
else:
|
||||
print "#include <debug.h>"
|
||||
print "#include <drivers/apple/gpio.h>"
|
||||
print "#include <platform.h>"
|
||||
print "#include <platform/soc/hwregbase.h>"
|
||||
print "#include <stdint.h>"
|
||||
if len(targets) > 1:
|
||||
print "#include <target/boardid.h>"
|
||||
print ""
|
||||
|
||||
# Create a mapping from enumeration values (8-bit) to the real value for the pin (32-bit)
|
||||
enum_map = {}
|
||||
for target in targets:
|
||||
for config in configs:
|
||||
enum_items = target_config_pinconfigs[target][config].to_enum_items()
|
||||
for key, value in enum_items:
|
||||
if key not in enum_map:
|
||||
enum_map[key] = value
|
||||
else:
|
||||
assert(enum_map[key] == value)
|
||||
|
||||
assert(len(enum_map) < 256)
|
||||
|
||||
# print the enumeration that will be used to index into the map
|
||||
print 'enum {'
|
||||
for key in sorted(enum_map.keys()):
|
||||
print '\t{},'.format(key)
|
||||
print '};\n\n'
|
||||
|
||||
# print the map itself
|
||||
print 'static const {} enum_map[] = {{'.format(array_type)
|
||||
for key in sorted(enum_map.keys()):
|
||||
print '\t[{}] = {},'.format(key, enum_map[key])
|
||||
print '};\n\n'
|
||||
|
||||
# print the pinconfigs, using the enumeration keys as the array items
|
||||
for target in targets:
|
||||
for config in configs:
|
||||
if len(targets) == 1:
|
||||
if len(configs) == 1:
|
||||
name = pinconfig_prefix
|
||||
else:
|
||||
name = '{}_{}'.format(pinconfig_prefix, config.lower())
|
||||
else:
|
||||
if len(configs) == 1:
|
||||
name = '{}_{}'.format(pinconfig_prefix, target.lower())
|
||||
else:
|
||||
name = '{}_{}{}'.format(pinconfig_prefix, target.lower(), config.lower())
|
||||
print target_config_pinconfigs[target][config].to_pinconfig_struct(name, 'uint8_t', num_domains, not header)
|
||||
|
||||
if header:
|
||||
print ""
|
||||
print "#endif /* __PLATFORM_PINCONFIG_H */"
|
||||
else:
|
||||
print 'struct pinconfig_map {'
|
||||
print '\tuint32_t board_id;'
|
||||
print '\tuint32_t board_id_mask;'
|
||||
print '\tconst uint8_t *pinconfigs[GPIOC_COUNT];'
|
||||
print '};'
|
||||
print ''
|
||||
print 'static const struct pinconfig_map cfg_map[] = {'
|
||||
if len(targets) == 1:
|
||||
if len(configs) == 1:
|
||||
# just one target and one config, so make a map that spits out the same pinconfig
|
||||
# for all board IDs
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_{}'.format(pinconfig_prefix, x), range(num_domains)))
|
||||
print '\t{{ 0, 0, {{ {} }} }},'.format(pinconfigs)
|
||||
elif len(configs) == 2:
|
||||
# One target, but ap/dev configs, so make a map that selects ap/dev based on the lsb
|
||||
# of the board ID
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_ap_{}'.format(pinconfig_prefix, x), range(num_domains)))
|
||||
print '\t{{ 0, 1, {{ {} }} }},'.format(pinconfigs)
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_dev_{}'.format(pinconfig_prefix, x), range(num_domains)))
|
||||
print '\t{{ 1, 1, {{ {} }} }},'.format(pinconfigs)
|
||||
else:
|
||||
assert False, 'Can\'t handle more than 2 configs per target'
|
||||
else:
|
||||
for target in targets:
|
||||
if len(configs) == 1:
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_{}_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains)))
|
||||
print '\t{{ TARGET_BOARD_ID_{}AP, ~1, {{ {} }} }},'.format(target.upper(), pinconfigs)
|
||||
elif len(configs) == 2:
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_{}ap_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains)))
|
||||
print '\t{{ TARGET_BOARD_ID_{}AP, ~0, {{ {} }} }},'.format(target.upper(), pinconfigs)
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_{}dev_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains)))
|
||||
print '\t{{ TARGET_BOARD_ID_{}DEV, ~0, {{ {} }} }},'.format(target.upper(), pinconfigs)
|
||||
else:
|
||||
assert False, 'Can\'t handle more than 2 configs per target'
|
||||
print '};'
|
||||
print ''
|
||||
print 'static {} *expanded_pinconfigs[GPIOC_COUNT];'.format(array_type)
|
||||
print ''
|
||||
print target_config_pinconfigs[targets[0]][configs[0]].to_size_array(num_domains)
|
||||
print ''
|
||||
if prefix is None:
|
||||
print 'const {} * target_get_default_gpio_cfg(uint32_t gpioc)'.format(array_type)
|
||||
else:
|
||||
print 'const {} * target_get_{}_gpio_cfg(int gpioc)'.format(array_type, prefix)
|
||||
print '{'
|
||||
print '\tstatic const struct pinconfig_map *selected_map = NULL;'
|
||||
print ''
|
||||
|
||||
print '\t/* Cannot use malloc as chunk manager is yet to be intialized */'
|
||||
|
||||
for domain_idx in range(num_domains):
|
||||
if domain_idx == 0:
|
||||
count_label = ''
|
||||
else:
|
||||
count_label = '_{}'.format(domain_idx)
|
||||
print '\tstatic {} pinconfig_buf{}[GPIO{}_GROUP_COUNT * GPIOPADPINS];'.format(array_type, count_label, count_label)
|
||||
print '\texpanded_pinconfigs[{}] = pinconfig_buf{};'.format(domain_idx, count_label)
|
||||
|
||||
print ''
|
||||
print '\tASSERT(gpioc < GPIOC_COUNT);'
|
||||
print ''
|
||||
print '\tif (selected_map == NULL) {';
|
||||
print '\t\tuint32_t board_id = platform_get_board_id();'
|
||||
print '\t\tfor (unsigned i = 0; i < sizeof(cfg_map)/sizeof(cfg_map[0]); i++) {'
|
||||
print '\t\t\tif ((board_id & cfg_map[i].board_id_mask) == cfg_map[i].board_id) {'
|
||||
print '\t\t\t\tselected_map = &cfg_map[i];'
|
||||
print '\t\t\t\tbreak;'
|
||||
print '\t\t\t}'
|
||||
print '\t\t}'
|
||||
print ''
|
||||
print '\t\tif (selected_map == NULL)'
|
||||
print '\t\t\tpanic("no default pinconfig for board id %u", board_id);'
|
||||
print ''
|
||||
print '\t\tfor (unsigned i = 0; i < GPIOC_COUNT; i++) {'
|
||||
print '\t\t\tuint32_t num_pins = controller_pins[i];'
|
||||
print ''
|
||||
print '\t\t\tfor (uint32_t j = 0; j < num_pins; j++) {'
|
||||
print '\t\t\t\tuint8_t enum_key = selected_map->pinconfigs[i][j];'
|
||||
print '\t\t\t\texpanded_pinconfigs[i][j] = enum_map[enum_key];'
|
||||
print '\t\t\t}'
|
||||
print '\t\t}'
|
||||
print '\t}'
|
||||
print ''
|
||||
print '\treturn expanded_pinconfigs[gpioc];'
|
||||
print '}'
|
||||
|
||||
|
||||
def main():
|
||||
soc_classes = collections.OrderedDict()
|
||||
soc_classes['h4a'] = H4APinConfig
|
||||
soc_classes['h5p'] = H5PPinConfig
|
||||
soc_classes['h5g'] = H5GPinConfig
|
||||
soc_classes['alcatraz'] = AlcatrazPinConfig
|
||||
soc_classes['h6'] = AlcatrazPinConfig
|
||||
soc_classes['h6p'] = AlcatrazPinConfig
|
||||
soc_classes['fiji'] = FijiPinConfig
|
||||
soc_classes['h7p'] = FijiPinConfig
|
||||
soc_classes['capri'] = CapriPinConfig
|
||||
soc_classes['h7g'] = CapriPinConfig
|
||||
soc_classes['m7'] = M7PinConfig
|
||||
soc_classes['h8p'] = MauiPinConfig
|
||||
soc_classes['maui'] = MauiPinConfig
|
||||
soc_classes['h8g'] = ElbaPinConfig
|
||||
soc_classes['elba'] = ElbaPinConfig
|
||||
soc_classes['h9p'] = CaymanPinConfig
|
||||
soc_classes['cayman'] = CaymanPinConfig
|
||||
soc_classes['m8'] = M8PinConfig
|
||||
|
||||
argparser = argparse.ArgumentParser(description='Converts CSV file to iBoot pinconfig.h format')
|
||||
argparser.add_argument('--soc', required=True, choices=soc_classes.keys())
|
||||
argparser.add_argument('--config-column', type=str, action='append')
|
||||
argparser.add_argument('--pupd-column', type=str, action='append')
|
||||
argparser.add_argument('--prefix', type=str, help='prefix for auto-generated variable and function names')
|
||||
argparser.add_argument('--copyright', type=str, default='YEAR', help='Year(s) to put in copyright notice')
|
||||
argparser.add_argument('--netname-column', type=str, action='append')
|
||||
argparser.add_argument('--radar', type=str, help='Link to spreadsheet tracker radar')
|
||||
argparser.add_argument('--force', action="store_true", help='attempt ouput even when there are parsing errors')
|
||||
argparser.add_argument('--rom', action="store_true", help='all pins except ROM function pins are disabled')
|
||||
argparser.add_argument('--header', action="store_true", help='generate a C header file instead of a C source file')
|
||||
argparser.add_argument('--sheet', default=None, help='sheet to read from when reading Excel files')
|
||||
argparser.add_argument('filename')
|
||||
|
||||
args = argparser.parse_args()
|
||||
|
||||
pinconfig_class = soc_classes[args.soc]
|
||||
|
||||
per_target_headers = collections.defaultdict(dict)
|
||||
|
||||
if not args.config_column is None:
|
||||
for item in map(lambda x: x.split(":"), args.config_column):
|
||||
if len(item) == 1:
|
||||
target = None
|
||||
column = item[0]
|
||||
elif len(item) == 2:
|
||||
target = item[0].upper()
|
||||
column = item[1]
|
||||
else:
|
||||
sys.stderr.write("Invalid config-column parameter\n")
|
||||
sys.exit(1)
|
||||
if 'function' in per_target_headers[target]:
|
||||
sys.stderr.write("Duplicate target in --config-column: \"{}\"".format(target))
|
||||
sys.exit(1)
|
||||
per_target_headers[target]['function'] = column
|
||||
|
||||
if not args.pupd_column is None:
|
||||
for item in map(lambda x: x.split(":"), args.pupd_column):
|
||||
if len(item) == 1:
|
||||
target = None
|
||||
column = item[0]
|
||||
elif len(item) == 2:
|
||||
target = item[0].upper()
|
||||
column = item[1]
|
||||
else:
|
||||
sys.stderr.write("Invalid pupd-column parameter\n")
|
||||
sys.exit(1)
|
||||
if 'pupd' in per_target_headers[target]:
|
||||
sys.stderr.write("Duplicate target in --pupd-column: \"{}\"".format(target))
|
||||
sys.exit(1)
|
||||
per_target_headers[target]['pupd'] = column
|
||||
|
||||
if not args.netname_column is None:
|
||||
for item in map(lambda x: x.split(":"), args.netname_column):
|
||||
if len(item) == 1:
|
||||
target = None
|
||||
column = item[0]
|
||||
elif len(item) == 2:
|
||||
target = item[0].upper()
|
||||
column = item[1]
|
||||
else:
|
||||
sys.stderr.write("Invalid netname-column parameter\n")
|
||||
sys.exit(1)
|
||||
if 'net_name' in per_target_headers[target]:
|
||||
sys.stderr.write("Duplicate target in --netname-column: \"{}\"".format(target))
|
||||
sys.exit(1)
|
||||
per_target_headers[target]['net_name'] = column
|
||||
|
||||
if not per_target_headers:
|
||||
per_target_headers[None] = {}
|
||||
|
||||
parser = CSVParser(pinconfig_class, per_target_headers)
|
||||
version, pinconfigs = parser.parse(args.filename, force=args.force, rom=args.rom, sheet=args.sheet)
|
||||
if pinconfigs is None:
|
||||
sys.exit(1)
|
||||
|
||||
def pretty_arg(arg):
|
||||
if arg == args.filename:
|
||||
return '<filename>'
|
||||
else:
|
||||
return pipes.quote(arg)
|
||||
commandline = './tools/csvtopinconfig.py {}'.format(' '.join(pretty_arg(a) for a in sys.argv[1:]))
|
||||
|
||||
print_pinconfigs(pinconfigs, pinconfig_class=pinconfig_class, version=version, radar=args.radar, copyright=args.copyright, commandline=commandline, prefix=args.prefix, rom=args.rom, header=args.header)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+2185
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/python
|
||||
import os
|
||||
import hashlib
|
||||
import hmac
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
argparser = argparse.ArgumentParser(description='Generates obfuscated logging file IDs for iBoot release builds')
|
||||
argparser.add_argument('--src', required=True, help='iBoot source tree')
|
||||
|
||||
args = argparser.parse_args()
|
||||
|
||||
id_file_map = {}
|
||||
|
||||
directory = args.src
|
||||
|
||||
base_hmac = hmac.new('obfuscation of filename for release build logging', '', hashlib.sha1)
|
||||
|
||||
for (path, dirnames, filenames) in os.walk(directory):
|
||||
# strip the part of the pathname we supplied off the pathname
|
||||
shortened_path = path[len(directory)+1:]
|
||||
# don't iterate into paths we don't care about
|
||||
|
||||
thisdir = os.path.split(shortened_path)[1]
|
||||
|
||||
if thisdir in ['build', '.git', '.svn']:
|
||||
del dirnames[:]
|
||||
continue
|
||||
for filename in filenames:
|
||||
# ignore file types we don't care about
|
||||
extension = os.path.splitext(filename)[1]
|
||||
if extension not in ['.c', '.cpp', '.s', '.S', '.h']:
|
||||
continue
|
||||
|
||||
full_path = os.path.join(shortened_path, filename)
|
||||
|
||||
current_hmac = base_hmac.copy()
|
||||
current_hmac.update(full_path)
|
||||
|
||||
file_id = current_hmac.hexdigest()[:16]
|
||||
|
||||
if file_id in id_file_map and id_file_map[file_id] != full_path:
|
||||
print >> sys.stderr, 'Error: Files "{}" and "{}" both have the same ID {}'.format(full_path, id_file_map[file_id], file_id)
|
||||
sys.exit(1)
|
||||
else:
|
||||
id_file_map[file_id] = full_path
|
||||
print '{}:{}'.format(full_path, file_id)
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
# iBoot gdb macros
|
||||
#
|
||||
# Vaguely inspired by the 'kgm' package.
|
||||
#
|
||||
|
||||
set print asm-demangle on
|
||||
set cp-abi gnu-v2
|
||||
set kaslr-memory-search off
|
||||
|
||||
echo Loading iBoot GDB macros package. Type "help igm" for more info.\n
|
||||
|
||||
define igm
|
||||
printf ""
|
||||
echo These are the gdb macros for iBoot debugging. Type "help igm" for more info.\n
|
||||
end
|
||||
|
||||
document igm
|
||||
| These are the iBoot gdb macros. These gdb macros are intended to be
|
||||
| used when debugging iBoot via jtag.
|
||||
|
|
||||
| The following macros are available in this package:
|
||||
|
|
||||
| showtasks Display a list of tasks
|
||||
| switchtotask Switch to different task
|
||||
| showallstacks Show stack of each tasks
|
||||
|
|
||||
| showheap Show blocks allocated in the heap
|
||||
|
|
||||
| Type "help <macro>" for more specific help on a particular macro.
|
||||
| Type "show user <macro>" to see what the macro is really doing.
|
||||
end
|
||||
|
||||
set $igm_mtype = sizeof(void *)
|
||||
set $igm_lp64 = ($igm_mtype == 8)
|
||||
|
||||
# Print a pointer
|
||||
define showptr
|
||||
if ($igm_lp64)
|
||||
printf "0x%016llx", $arg0
|
||||
else
|
||||
printf "0x%08x", $arg0
|
||||
end
|
||||
end
|
||||
|
||||
# Traverse a list expected to be the queue_entry list from a task, and print anything
|
||||
# found on the list that is not a task; it should be the event being waited on.
|
||||
define print_non_tasks_in_list
|
||||
set $igm_ntil_terminal = (struct list_node *)$arg0
|
||||
set $igm_ntil_cursor = $igm_ntil_terminal->next
|
||||
while ($igm_ntil_cursor != $igm_ntil_terminal)
|
||||
if ($igm_lp64)
|
||||
set $igm_ntil_task = (struct task *)((void *)$igm_ntil_cursor - 20)
|
||||
if ($igm_ntil_task != 0x7461736b)
|
||||
set $igm_ntil_event = (struct task_event *)((void *)$igm_ntil_cursor - 8)
|
||||
showptr $igm_ntil_event
|
||||
end
|
||||
else
|
||||
set $igm_ntil_task = (struct task *)((void *)$igm_ntil_cursor - 12)
|
||||
if ($igm_ntil_task != 0x7461736b)
|
||||
set $igm_ntil_event = (struct task_event *)((void *)$igm_ntil_cursor - 8)
|
||||
showptr $igm_ntil_event
|
||||
end
|
||||
end
|
||||
set $igm_ntil_cursor = $igm_ntil_cursor->next
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
define showtask
|
||||
showptr $arg0
|
||||
set $igm_task = (struct task *)$arg0
|
||||
if (($igm_task->magic != 0x7461736b) || ($igm_task->magic2 != 0x74736b32))
|
||||
printf " not a valid task\n"
|
||||
else
|
||||
printf " %16s: ", &$igm_task->name
|
||||
if ($igm_task->state == 0)
|
||||
printf "INITIAL"
|
||||
end
|
||||
if ($igm_task->state == 1)
|
||||
printf "READY"
|
||||
end
|
||||
if ($igm_task->state == 2)
|
||||
printf "RUNNING"
|
||||
end
|
||||
if ($igm_task->state == 3)
|
||||
printf "BLOCKED on event "
|
||||
print_non_tasks_in_list &$igm_task->queue_node
|
||||
end
|
||||
if ($igm_task->state == 4)
|
||||
printf "SLEEPING for "
|
||||
printf "%lluus ", $igm_task->sleep_callout.delay
|
||||
printf "@ 0x%016llx", $igm_task->sleep_callout.sched_ticks
|
||||
end
|
||||
if ($igm_task->state == 5)
|
||||
printf "FINISHED"
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
end
|
||||
|
||||
define showtasks
|
||||
showtask &bootstrap_task
|
||||
set $igm_task_cursor = (struct list_node *)task_list->next
|
||||
while $igm_task_cursor != &task_list
|
||||
if ($igm_lp64)
|
||||
set $igm_task_task = ((void *)$igm_task_cursor - 8)
|
||||
else
|
||||
set $igm_task_task = ((void *)$igm_task_cursor - 4)
|
||||
end
|
||||
showtask $igm_task_task
|
||||
set $igm_task_cursor = $igm_task_cursor->next
|
||||
end
|
||||
end
|
||||
|
||||
document showtasks
|
||||
| Print a summary listing of all tasks.
|
||||
end
|
||||
|
||||
define dobacktrace
|
||||
set $_task = (struct task*)$arg0
|
||||
set $_fp = (void **)$_task->arch.fp
|
||||
printf "%s: \n", $_task->name
|
||||
while $_fp != 0
|
||||
set $_pc = *($_fp + 1)
|
||||
x/i $_pc
|
||||
set $_fp = (void**)*$_fp
|
||||
end
|
||||
end
|
||||
|
||||
define showallstacks
|
||||
if ($igm_lp64)
|
||||
set $igm_task_cursor = (struct list_node *)task_list->next
|
||||
while $igm_task_cursor != &task_list
|
||||
if ($igm_lp64)
|
||||
set $igm_task_task = ((void *)$igm_task_cursor - 8)
|
||||
else
|
||||
set $igm_task_task = ((void *)$igm_task_cursor - 4)
|
||||
end
|
||||
dobacktrace $igm_task_task
|
||||
printf "-------------------------------------\n"
|
||||
set $igm_task_cursor = $igm_task_cursor->next
|
||||
end
|
||||
else
|
||||
printf "XXX Not supported yet\n"
|
||||
end
|
||||
end
|
||||
|
||||
document showallstacks
|
||||
| Routine to print out the stack for each tasks in the system.
|
||||
end
|
||||
|
||||
# XXX doesn't seems to work
|
||||
define switchtotask
|
||||
printf "XXX Not supported yet\n"
|
||||
set $igm_newtask = (struct task *)$arg0
|
||||
if (($igm_newtask->magic != 0x7461736b) || ($igm_newtask->magic2 != 0x74736b32))
|
||||
printf "not a valid task\n"
|
||||
else
|
||||
if ($igm_lp64)
|
||||
set $igm_arch_state = &$igm_newtask->arch
|
||||
set $r16 = $igm_arch_state->regs[15]
|
||||
set $r17 = $igm_arch_state->regs[16]
|
||||
set $r19 = $igm_arch_state->regs[18]
|
||||
set $r20 = $igm_arch_state->regs[19]
|
||||
set $r21 = $igm_arch_state->regs[20]
|
||||
set $r22 = $igm_arch_state->regs[21]
|
||||
set $r23 = $igm_arch_state->regs[22]
|
||||
set $r24 = $igm_arch_state->regs[23]
|
||||
set $r25 = $igm_arch_state->regs[24]
|
||||
set $r26 = $igm_arch_state->regs[25]
|
||||
set $r27 = $igm_arch_state->regs[26]
|
||||
set $r28 = $igm_arch_state->regs[27]
|
||||
set $r29 = $igm_arch_state->fp
|
||||
set $r30 = $igm_arch_state->lr
|
||||
set $r31 = $igm_arch_state->sp
|
||||
flushregs
|
||||
flushstack
|
||||
else
|
||||
set $igm_arch_state = &$igm_newtask->arch
|
||||
set $r4 = $igm_arch_state->regs[0]
|
||||
set $r5 = $igm_arch_state->regs[1]
|
||||
set $r6 = $igm_arch_state->regs[2]
|
||||
set $r7 = $igm_arch_state->regs[3]
|
||||
set $r8 = $igm_arch_state->regs[4]
|
||||
set $r9 = $igm_arch_state->regs[5]
|
||||
set $r10 = $igm_arch_state->regs[6]
|
||||
set $r11 = $igm_arch_state->regs[7]
|
||||
set $r13 = $igm_arch_state->regs[8]
|
||||
set $r14 = $igm_arch_state->regs[9]
|
||||
flushregs
|
||||
flushstack
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
document switchtotask
|
||||
Syntax: switchtotask <task address>
|
||||
| Sets GDB's idea of the current task to <task address>
|
||||
end
|
||||
|
||||
define show_heap_chunk
|
||||
set $chunk_ptr = (struct chunk_data *)$arg0
|
||||
printf "chunk @"
|
||||
showptr $chunk_ptr->chunk_base
|
||||
printf "/0x%x\n", $chunk_ptr->chunk_size
|
||||
|
||||
printf " block size type ptr len\n"
|
||||
set $index = 1
|
||||
while $index != 0
|
||||
|
||||
# compute the block pointer from the index
|
||||
set $block_ptr = (struct heap_block *)($chunk_ptr->chunk_base + $index)
|
||||
|
||||
if $block_ptr->this_size == 1
|
||||
# this is the end of the chunk
|
||||
loop_break
|
||||
end
|
||||
|
||||
# print block address and size
|
||||
showptr $block_ptr
|
||||
printf " 0x%08x", $block_ptr->this_size
|
||||
|
||||
# decode and print block contents
|
||||
if $block_ptr->this_free != 0
|
||||
printf " free "
|
||||
else
|
||||
printf " malloc "
|
||||
showptr $block_ptr + 1
|
||||
end
|
||||
printf " 0x%08x\n", ($block_ptr->this_size - 1) * sizeof(struct heap_block)
|
||||
|
||||
# XXX range-check
|
||||
set $index = $index + $block_ptr->this_size
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
define showheap
|
||||
# Iterate chunks and print these first
|
||||
# XXX might be good to have the heap use more unique names
|
||||
set $chunk_iterator = 0
|
||||
while $chunk_iterator < chunk_count
|
||||
show_heap_chunk &lead[$chunk_iterator]
|
||||
set $chunk_iterator = $chunk_iterator + 1
|
||||
end
|
||||
end
|
||||
|
||||
document showheap
|
||||
| Print the contents of the heap.
|
||||
end
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2009 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach/arm/thread_status.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
struct task {
|
||||
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 */
|
||||
arm_thread_state_t arm_state; /* thread state for this flavor */
|
||||
};
|
||||
|
||||
|
||||
int main (int argc, const char *argv[])
|
||||
{
|
||||
FILE *out;
|
||||
FILE *in;
|
||||
DIR *dir;
|
||||
const char* src_dir;
|
||||
int num_segments;
|
||||
u_int32_t data_cursor;
|
||||
struct dirent *ep;
|
||||
struct mach_header header =
|
||||
{
|
||||
MH_MAGIC,
|
||||
CPU_TYPE_ARM,
|
||||
CPU_SUBTYPE_ARM_V4T,
|
||||
MH_CORE
|
||||
};
|
||||
struct task thread = {0};
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("%s <iop.memory dir> <output corefile>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open directory
|
||||
src_dir = argv[1];
|
||||
dir = opendir(src_dir);
|
||||
if (!dir)
|
||||
return -2;
|
||||
|
||||
// Open output core file
|
||||
out = fopen(argv[2], "wb");
|
||||
if (!out)
|
||||
return -3;
|
||||
|
||||
// Lay down a heder (to be updated later)
|
||||
fwrite(&header, 1, sizeof(header), out);
|
||||
|
||||
// Lay down some thread context
|
||||
thread.cmd = LC_THREAD;
|
||||
thread.cmdsize = sizeof(thread);
|
||||
|
||||
thread.flavor = ARM_THREAD_STATE;
|
||||
thread.count = ARM_THREAD_STATE_COUNT;
|
||||
|
||||
// XXX fill in registers
|
||||
// thread.arm_state.r[13] =
|
||||
// thread.arm_state.sp =
|
||||
// thread.arm_state.lr =
|
||||
// thread.arm_state.pc =
|
||||
// thread.arm_state.cpsr =
|
||||
|
||||
fwrite(&thread, 1, sizeof(thread), out);
|
||||
|
||||
header.ncmds += 1;
|
||||
header.sizeofcmds += sizeof(thread);
|
||||
|
||||
// To make life easier below, count the number of segments we're going to have
|
||||
num_segments = 0;
|
||||
while(ep = readdir(dir))
|
||||
{
|
||||
int addr;
|
||||
if (1 == sscanf(ep->d_name, "%x.bin", &addr))
|
||||
num_segments += 1;
|
||||
}
|
||||
// Rewind to actually process these files now
|
||||
// XXX assume contents will not change from the scan above
|
||||
closedir(dir);
|
||||
dir = opendir(src_dir);
|
||||
|
||||
// For each file, create a segment based on its filename
|
||||
chdir(src_dir);
|
||||
data_cursor = sizeof(header) +
|
||||
header.sizeofcmds +
|
||||
(num_segments * sizeof(struct segment_command));
|
||||
while(ep = readdir(dir))
|
||||
{
|
||||
struct segment_command sc = {0};
|
||||
|
||||
// Filenames are <address>.bin
|
||||
if (1 == sscanf(ep->d_name, "%x.bin", &sc.vmaddr))
|
||||
{
|
||||
// Create a segment with the given address and file contents
|
||||
in = fopen(ep->d_name, "rb");
|
||||
if (in)
|
||||
{
|
||||
void * data;
|
||||
int old_pos;
|
||||
|
||||
// Record how big the source file is
|
||||
fseek(in, 0, SEEK_END);
|
||||
sc.vmsize = ftell(in);
|
||||
fseek(in, 0, SEEK_SET);
|
||||
|
||||
// Set up a zero-section text segment load command
|
||||
sc.cmd = LC_SEGMENT;
|
||||
sc.cmdsize = sizeof(sc);
|
||||
strcpy(sc.segname, SEG_TEXT);
|
||||
|
||||
// Populate the entire segment, no gaps
|
||||
// Use the data cursor pointing past the end of the load commands
|
||||
sc.filesize = sc.vmsize;
|
||||
sc.fileoff = data_cursor;
|
||||
data_cursor += sc.filesize;
|
||||
|
||||
// Write out the load command
|
||||
fwrite(&sc, 1, sizeof(sc), out);
|
||||
|
||||
|
||||
// Read out the data
|
||||
data = malloc(sc.vmsize);
|
||||
if (!data)
|
||||
return -4;
|
||||
fread(data, 1, sc.vmsize, in);
|
||||
fclose(in);
|
||||
|
||||
// Put the data at the current cursor command
|
||||
old_pos = ftell(out);
|
||||
if (fseek(out, sc.fileoff, SEEK_SET))
|
||||
return -5;
|
||||
fwrite(data, 1, sc.vmsize, out);
|
||||
free(data);
|
||||
|
||||
// Put the cursor back for the next load command
|
||||
fseek(out, old_pos, SEEK_SET);
|
||||
|
||||
// Increment the number of load commands in the mach header
|
||||
header.ncmds += 1;
|
||||
header.sizeofcmds += sizeof(sc);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "can't open %s\n", ep->d_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
// Update the header with how many segments we actually generated
|
||||
fseek(out, 0, SEEK_SET);
|
||||
fwrite(&header, 1, sizeof(header), out);
|
||||
fclose(out);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
print "Loading iBoot debugging from %s" % __file__
|
||||
self_path = str(__file__)
|
||||
base_dir_name = self_path[:self_path.rfind("/")]
|
||||
core_os_plugin = base_dir_name + "/lldb_os_iboot.py"
|
||||
osplugin_cmd = "settings set target.process.python-os-plugin-path \"%s\"" % core_os_plugin
|
||||
print osplugin_cmd
|
||||
debugger.HandleCommand(osplugin_cmd)
|
||||
thread_fmt_cmd = 'settings set thread-format thread #${thread.index}{ "${thread.name}"}: tid = ${thread.id}{, ${frame.pc}}{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}{ at ${line.file.basename}:${line.number}}{, stop reason = ${thread.stop-reason}}{\nReturn value: ${thread.return-value}}\n'
|
||||
debugger.HandleCommand(thread_fmt_cmd)
|
||||
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import lldb
|
||||
import struct
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
debugger.HandleCommand("command script add -f lldb_os_iboot.iboot_tasks iboot_tasks")
|
||||
|
||||
def iboot_tasks(debugger, command, result, internal_dict):
|
||||
"""Print the list of tasks maintained by the iBoot runtime"""
|
||||
process = debugger.GetSelectedTarget().GetProcess()
|
||||
if not process or not process.target:
|
||||
print 'Not connected'
|
||||
return
|
||||
|
||||
plugin = OperatingSystemPlugIn(process)
|
||||
|
||||
current_task = plugin.get_global('current_task')
|
||||
assert current_task is not None
|
||||
current_task_addr = current_task.GetValueAsUnsigned()
|
||||
|
||||
tasks = plugin.get_task_dict().itervalues()
|
||||
print ' TID Task Name Address'
|
||||
print ' ------ ---- ---------------- ------------------'
|
||||
for t in tasks:
|
||||
task_addr = t.AddressOf().GetValueAsUnsigned()
|
||||
current = "*" if task_addr == current_task_addr else " "
|
||||
|
||||
tv = lldb.value(t)
|
||||
task_id = int(tv.task_id)
|
||||
tid = task_id + plugin.TID_OFFSET
|
||||
name = plugin.value_to_str(tv.name.sbvalue)
|
||||
|
||||
print '{} {:#06x} {:<4} {:<16} {:#018x}'.format(current, tid, task_id, name, task_addr)
|
||||
|
||||
class OperatingSystemPlugIn(object):
|
||||
"""Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class"""
|
||||
|
||||
TID_OFFSET = 0x1000
|
||||
|
||||
def __init__(self, process):
|
||||
'''Initialization needs a valid.SBProcess object'''
|
||||
self.process = None
|
||||
self.registers = None
|
||||
self.task_dict = None
|
||||
self.arch = None
|
||||
if type(process) is lldb.SBProcess and process.IsValid():
|
||||
self.process = process
|
||||
triple = process.target.triple
|
||||
if triple:
|
||||
self.arch = triple.split('-')[0]
|
||||
|
||||
def get_global(self, name):
|
||||
return self.process.target.FindGlobalVariables(name, 1)[0]
|
||||
|
||||
def iboot_task_iter(self, head, skip_task=None):
|
||||
task_type = self.process.target.FindFirstType('task')
|
||||
task_members = { member.name : member for member in task_type.get_members_array() }
|
||||
list_offset = task_members['task_list_node'].byte_offset
|
||||
|
||||
if not skip_task is None:
|
||||
skip_addr = skip_task.GetValueAsUnsigned()
|
||||
else:
|
||||
skip_addr = 0
|
||||
|
||||
for node in head.AddressOf().linked_list_iter('next'):
|
||||
task_addr = node.GetValueAsUnsigned() - list_offset
|
||||
if task_addr == skip_addr:
|
||||
continue
|
||||
task = node.CreateValueFromAddress('task', task_addr, task_type)
|
||||
tid = int(lldb.value(task).task_id)
|
||||
yield tid, task
|
||||
|
||||
def summarize_iboot_task(self, task):
|
||||
task_value = lldb.value(task)
|
||||
return {
|
||||
'name': self.value_to_str(task_value.name.sbvalue).ljust(15),
|
||||
'tid': int(task_value.task_id) + self.TID_OFFSET,
|
||||
'state': 'stopped',
|
||||
'stop_reason': 'none'
|
||||
}
|
||||
|
||||
def get_task_dict(self, skip_current=False):
|
||||
if self.task_dict is None:
|
||||
task_list = self.get_global('task_list')
|
||||
assert task_list is not None
|
||||
|
||||
if skip_current:
|
||||
skip_task = self.get_global('current_task')
|
||||
else:
|
||||
skip_task = None
|
||||
|
||||
task_tuples = list(self.iboot_task_iter(task_list, skip_task))
|
||||
# The first entry in the list isn't a task, so remove it from the list
|
||||
task_tuples = task_tuples[1:]
|
||||
|
||||
# If there aren't any tasks in the list, we must still be
|
||||
# in the bootstrap task, which we can get through current_task
|
||||
if len(task_tuples) == 0 and not skip_current:
|
||||
current_task = self.get_global('current_task').Dereference()
|
||||
current_tid = int(lldb.value(current_task).task_id)
|
||||
task_tuples = [(current_tid, current_task)]
|
||||
|
||||
self.task_dict = {tid : thread for tid,thread in task_tuples}
|
||||
return self.task_dict
|
||||
|
||||
@staticmethod
|
||||
def value_to_str(value):
|
||||
return "".join([chr(x) for x in value.GetData().uint8s if x != 0])
|
||||
|
||||
@staticmethod
|
||||
def value_to_bytes(value):
|
||||
return "".join([chr(x) for x in value.GetData().uint8s])
|
||||
|
||||
def get_thread_info(self):
|
||||
task_iter = self.get_task_dict(skip_current=True).itervalues()
|
||||
return map(lambda x: self.summarize_iboot_task(x), task_iter)
|
||||
|
||||
def get_register_data(self, tid):
|
||||
assert tid >= self.TID_OFFSET
|
||||
tasks = self.get_task_dict()
|
||||
task = tasks[tid - self.TID_OFFSET]
|
||||
|
||||
task_arch_value = lldb.value(task).arch
|
||||
|
||||
if self.arch == 'arm64':
|
||||
regs = task_arch_value.regs.sbvalue
|
||||
fp = task_arch_value.fp.sbvalue
|
||||
lr = task_arch_value.lr.sbvalue
|
||||
sp = task_arch_value.sp.sbvalue
|
||||
# We don't know the PC, so use the LR instead to keep LLDB happy
|
||||
pc = task_arch_value.lr.sbvalue
|
||||
registers = [regs, fp, lr, sp, pc]
|
||||
else:
|
||||
regs = task_arch_value.regs.sbvalue
|
||||
# We don't know the PC, so use the LR instead to keep LLDB happy
|
||||
pc = task_arch_value.regs[9].sbvalue
|
||||
registers = [regs, pc]
|
||||
|
||||
result = "".join(map(self.value_to_bytes, registers))
|
||||
return result
|
||||
|
||||
def get_register_info(self):
|
||||
if self.registers == None:
|
||||
self.registers = dict()
|
||||
if self.arch == 'arm64':
|
||||
self.registers['sets'] = ['GPR']
|
||||
self.registers['registers'] = [
|
||||
{ 'name':'x0' , 'bitsize' : 64, 'offset' : 0x00, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 0, 'dwarf' : 0},
|
||||
{ 'name':'x1' , 'bitsize' : 64, 'offset' : 0x08, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 1, 'dwarf' : 1},
|
||||
{ 'name':'x2' , 'bitsize' : 64, 'offset' : 0x10, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 2, 'dwarf' : 2},
|
||||
{ 'name':'x3' , 'bitsize' : 64, 'offset' : 0x18, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 3, 'dwarf' : 3},
|
||||
{ 'name':'x4' , 'bitsize' : 64, 'offset' : 0x20, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 4, 'dwarf' : 4},
|
||||
{ 'name':'x5' , 'bitsize' : 64, 'offset' : 0x28, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 5, 'dwarf' : 5},
|
||||
{ 'name':'x6' , 'bitsize' : 64, 'offset' : 0x30, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 6, 'dwarf' : 6},
|
||||
{ 'name':'x7' , 'bitsize' : 64, 'offset' : 0x38, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 7, 'dwarf' : 7},
|
||||
{ 'name':'x8' , 'bitsize' : 64, 'offset' : 0x40, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 8, 'dwarf' : 8},
|
||||
{ 'name':'x9' , 'bitsize' : 64, 'offset' : 0x48, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 9, 'dwarf' : 9},
|
||||
{ 'name':'x10' , 'bitsize' : 64, 'offset' : 0x50, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 10, 'dwarf' : 10},
|
||||
{ 'name':'x11' , 'bitsize' : 64, 'offset' : 0x58, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 11, 'dwarf' : 11},
|
||||
{ 'name':'x12' , 'bitsize' : 64, 'offset' : 0x60, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 12, 'dwarf' : 12},
|
||||
{ 'name':'x13' , 'bitsize' : 64, 'offset' : 0x68, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 13, 'dwarf' : 13},
|
||||
{ 'name':'x14' , 'bitsize' : 64, 'offset' : 0x70, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 14, 'dwarf' : 14},
|
||||
{ 'name':'x15' , 'bitsize' : 64, 'offset' : 0x78, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 15, 'dwarf' : 15},
|
||||
{ 'name':'x16' , 'bitsize' : 64, 'offset' : 0x80, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 16, 'dwarf' : 16},
|
||||
{ 'name':'x17' , 'bitsize' : 64, 'offset' : 0x88, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 17, 'dwarf' : 17},
|
||||
{ 'name':'x18' , 'bitsize' : 64, 'offset' : 0x90, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 18, 'dwarf' : 18},
|
||||
{ 'name':'x19' , 'bitsize' : 64, 'offset' : 0x98, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 19, 'dwarf' : 19},
|
||||
{ 'name':'x20' , 'bitsize' : 64, 'offset' : 0xa0, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 20, 'dwarf' : 20},
|
||||
{ 'name':'x21' , 'bitsize' : 64, 'offset' : 0xa8, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 21, 'dwarf' : 21},
|
||||
{ 'name':'x22' , 'bitsize' : 64, 'offset' : 0xb0, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 22, 'dwarf' : 22},
|
||||
{ 'name':'x23' , 'bitsize' : 64, 'offset' : 0xb8, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 23, 'dwarf' : 23},
|
||||
{ 'name':'x24' , 'bitsize' : 64, 'offset' : 0xc0, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 24, 'dwarf' : 24},
|
||||
{ 'name':'x25' , 'bitsize' : 64, 'offset' : 0xc8, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 25, 'dwarf' : 25},
|
||||
{ 'name':'x26' , 'bitsize' : 64, 'offset' : 0xd0, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 26, 'dwarf' : 26},
|
||||
{ 'name':'x27' , 'bitsize' : 64, 'offset' : 0xd8, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 27, 'dwarf' : 27},
|
||||
{ 'name':'x28' , 'bitsize' : 64, 'offset' : 0xe0, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 28, 'dwarf' : 28},
|
||||
{ 'name':'fp' , 'bitsize' : 64, 'offset' : 0xe8, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 29, 'dwarf' : 29, 'generic':'fp'},
|
||||
{ 'name':'lr' , 'bitsize' : 64, 'offset' : 0xf0, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 30, 'dwarf' : 30, 'generic':'lr'},
|
||||
{ 'name':'sp' , 'bitsize' : 64, 'offset' : 0xf8, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 31, 'dwarf' : 31, 'generic':'sp'},
|
||||
{ 'name':'pc' , 'bitsize' : 64, 'offset' : 0x100, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 32, 'dwarf' : 32, 'generic':'pc'},
|
||||
]
|
||||
else:
|
||||
self.registers['sets'] = ['GPR']
|
||||
self.registers['registers'] = [
|
||||
{ 'name':'r4' , 'bitsize' : 32, 'offset' : 0x00, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 4, 'dwarf' : 4},
|
||||
{ 'name':'r5' , 'bitsize' : 32, 'offset' : 0x04, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 5, 'dwarf' : 5},
|
||||
{ 'name':'r6' , 'bitsize' : 32, 'offset' : 0x08, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 6, 'dwarf' : 6},
|
||||
{ 'name':'r7' , 'bitsize' : 32, 'offset' : 0x0c, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 7, 'dwarf' : 7, 'generic':'fp'},
|
||||
{ 'name':'r8' , 'bitsize' : 32, 'offset' : 0x10, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 8, 'dwarf' : 8},
|
||||
{ 'name':'r9' , 'bitsize' : 32, 'offset' : 0x14, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 9, 'dwarf' : 9},
|
||||
{ 'name':'r10' , 'bitsize' : 32, 'offset' : 0x18, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 10, 'dwarf' : 10},
|
||||
{ 'name':'r11' , 'bitsize' : 32, 'offset' : 0x1c, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 11, 'dwarf' : 11},
|
||||
{ 'name':'r13' , 'bitsize' : 32, 'offset' : 0x20, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 13, 'dwarf' : 13, 'generic':'sp'},
|
||||
{ 'name':'r14' , 'bitsize' : 32, 'offset' : 0x24, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 14, 'dwarf' : 14, 'generic':'lr'},
|
||||
{ 'name':'pc' , 'bitsize' : 32, 'offset' : 0x28, 'encoding':'uint' , 'format':'hex', 'set': 0, 'gcc' : 15, 'dwarf' : 15, 'generic':'pc'},
|
||||
]
|
||||
return self.registers
|
||||
|
||||
+946
@@ -0,0 +1,946 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 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.
|
||||
#
|
||||
|
||||
#
|
||||
# A module for manipulating statically linked mach-o files.
|
||||
#
|
||||
# Load information from a mach-o file:
|
||||
#
|
||||
# f = macho.File(pathname)
|
||||
#
|
||||
# Search for non-debug global text symbols starting with 'a' :
|
||||
#
|
||||
# m = { "name": "^_a.*", "section": "__TEXT,.*", n_type": (N_STAB | N_EXT, N_EXT, N_STAB)}
|
||||
# l = f.symtab.matching(m)
|
||||
#
|
||||
# Read a structure pointed to by a symbol
|
||||
#
|
||||
# d = l.unpack("IIQ")
|
||||
#
|
||||
# Write changes back to the file
|
||||
#
|
||||
# f.update
|
||||
#
|
||||
# Specific things this module does not support:
|
||||
# - moving anything
|
||||
# - adding, resizing or removing sections or segments
|
||||
#
|
||||
#
|
||||
|
||||
import io
|
||||
import struct
|
||||
import re
|
||||
|
||||
# section types/attributes
|
||||
SECTION_TYPE = 0x000000ff # 256 section types
|
||||
SECTION_ATTRIBUTES = 0xffffff00 # 24 section attributes
|
||||
|
||||
S_REGULAR = 0x00 # regular section
|
||||
S_ZEROFILL = 0x01 # zero fill on demand section
|
||||
S_CSTRING_LITERALS = 0x02 # section with only literal C strings
|
||||
S_4BYTE_LITERALS = 0x03 # section with only 4 byte literals
|
||||
S_8BYTE_LITERALS = 0x04 # section with only 8 byte literals
|
||||
S_LITERAL_POINTERS = 0x05 # section with only pointers to
|
||||
S_NON_LAZY_SYMBOL_POINTERS = 0x06 # section with only non-lazy symbol pointers
|
||||
S_LAZY_SYMBOL_POINTERS = 0x07 # section with only lazy symbol pointers
|
||||
S_SYMBOL_STUBS = 0x08 # section with only symbol stubs, byte size of stub in the reserved2 field
|
||||
S_MOD_INIT_FUNC_POINTERS = 0x09 # section with only function pointers for initializatio
|
||||
S_MOD_TERM_FUNC_POINTERS = 0x0a # section with only function pointers for termination
|
||||
S_COALESCED = 0x0b # section contains symbols that are to be coalesced
|
||||
S_GB_ZEROFILL = 0x0c # zero fill on demand section (that can be larger than 4 gigabytes)
|
||||
S_INTERPOSING = 0x0d # section with only pairs of function pointers for interposing
|
||||
S_16BYTE_LITERALS = 0x0e # section with only 16 byte literals
|
||||
S_DTRACE_DOF = 0x0f # section contains DTrace Object Format
|
||||
S_THREAD_LOCAL_REGULAR = 0x11 # template of initial values for TLVs
|
||||
S_THREAD_LOCAL_ZEROFILL = 0x12 # template of initial values for TLVs
|
||||
S_THREAD_LOCAL_VARIABLES = 0x13 # TLV descriptors
|
||||
S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14 # pointers to TLV descriptors
|
||||
S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15 # functions to call to initialize TLV values
|
||||
|
||||
SECTION_ATTRIBUTES_USR = 0xff000000 # User setable attributes
|
||||
S_ATTR_PURE_INSTRUCTIONS = 0x80000000 # section contains only true machine instructions
|
||||
S_ATTR_NO_TOC = 0x40000000 # section contains coalesced symbols that are not to be in a ranlib table of contents
|
||||
S_ATTR_STRIP_STATIC_SYMS = 0x20000000 # ok to strip static symbols in this section in files with the MH_DYLDLINK flag
|
||||
S_ATTR_NO_DEAD_STRIP = 0x10000000 # no dead stripping
|
||||
S_ATTR_LIVE_SUPPORT = 0x08000000 # blocks are live if they reference live blocks
|
||||
S_ATTR_SELF_MODIFYING_CODE = 0x04000000 # Used with i386 code stubs written on by dyld
|
||||
S_ATTR_DEBUG = 0x02000000 # a debug section
|
||||
SECTION_ATTRIBUTES_SYS = 0x00ffff00 # system setable attributes
|
||||
S_ATTR_SOME_INSTRUCTIONS = 0x00000400 # section contains some machine instructions
|
||||
S_ATTR_EXT_RELOC = 0x00000200 # section has external relocation entries
|
||||
S_ATTR_LOC_RELOC = 0x00000100 # section has local relocation entries
|
||||
|
||||
SEG_PAGEZERO = "__PAGEZERO" # the pagezero segment which has no protections and catches NULL references
|
||||
SEG_TEXT = "__TEXT" # the tradition UNIX text segment */
|
||||
SECT_TEXT = "__text" # the real text part of the text section no headers, and no padding */
|
||||
SEG_DATA = "__DATA" # the tradition UNIX data segment */
|
||||
SECT_DATA = "__data" # the real initialized data section, no padding, no bss overlap */
|
||||
SECT_BSS = "__bss" # the real uninitialized data section, no padding */
|
||||
SECT_COMMON = "__common" # the section common symbols are allocated in by the link editor */
|
||||
|
||||
# not-a-section value for n_sect
|
||||
NO_SECT = 0x00 # section numbers start at 1
|
||||
|
||||
# nlist n_type values
|
||||
N_STAB = 0xe0 # mask for debugger symbols
|
||||
N_TYPE = 0x0e # mask for symbol type
|
||||
|
||||
N_PEXT = 0x10 # symbol is private-extern
|
||||
N_EXT = 0x01 # symbol is external
|
||||
|
||||
N_UNDF = 0x00 # symbol is undefined
|
||||
N_ABS = 0x02 # absolute (n_sect is NO_SECT)
|
||||
#N_TEXT = 0x04 # text symbol
|
||||
N_DATA = 0x06 # data symbol
|
||||
#N_BSS = 0x08 # bss symbol
|
||||
N_INDR = 0x0a # symbol is indirect (n_value is string table index)
|
||||
N_SECT = 0x0e # symbol is in n_sect
|
||||
#N_COMM = 0x12 # common symbol
|
||||
#N_FN = 0x1e # filename
|
||||
|
||||
# nlist n_desc values
|
||||
N_WEAK_REF = 0x40 # weak reference (value may be zero)
|
||||
N_WEAK_DEF = 0x80 # weak definition
|
||||
N_ARM_THUMB_DEF = 0x0008 # Thumb function
|
||||
|
||||
# selected STAB symbol types
|
||||
# fields listed here are n_name, n_sect, n_desc, n_value
|
||||
N_GSYM = 0x20 # global symbol: name,NO_SECT,type,0
|
||||
N_FUN = 0x24 # procedure: name,n_sect,linenumber,address
|
||||
N_STSYM = 0x26 # static symbol: name,n_sect,type,address
|
||||
N_LCSYM = 0x28 # .lcomm symbol: name,n_sect,type,address
|
||||
N_BNSYM = 0x2e # begin nsect sym: 0,n_sect,0,address
|
||||
N_OPT = 0x3c # emitted with gcc2_compiled and in gcc source
|
||||
N_RSYM = 0x40 # register sym: name,NO_SECT,type,register
|
||||
N_SLINE = 0x44 # src line: 0,n_sect,linenumber,address
|
||||
N_ENSYM = 0x4e # end nsect sym: 0,n_sect,0,address
|
||||
N_SSYM = 0x60 # structure elt: name,NO_SECT,type,struct_offset
|
||||
N_SO = 0x64 # source file name: name,n_sect,0,address
|
||||
N_OSO = 0x66 # object file name: name,0,0,st_mtime
|
||||
N_LSYM = 0x80 # local sym: name,NO_SECT,type,offset
|
||||
N_BINCL = 0x82 # include file beginning: name,NO_SECT,0,sum
|
||||
N_SOL = 0x84 # #included file name: name,n_sect,0,address
|
||||
N_PARAMS = 0x86 # compiler parameters: name,NO_SECT,0,0
|
||||
N_VERSION = 0x88 # compiler version: name,NO_SECT,0,0
|
||||
N_OLEVEL = 0x8A # compiler -O level: name,NO_SECT,0,0
|
||||
N_PSYM = 0xa0 # parameter: name,NO_SECT,type,offset
|
||||
N_EINCL = 0xa2 # include file end: name,NO_SECT,0,0
|
||||
N_ENTRY = 0xa4 # alternate entry: name,n_sect,linenumber,address
|
||||
N_LBRAC = 0xc0 # left bracket: 0,NO_SECT,nesting level,address
|
||||
N_EXCL = 0xc2 # deleted include file: name,NO_SECT,0,sum
|
||||
N_RBRAC = 0xe0 # right bracket: 0,NO_SECT,nesting level,address
|
||||
N_BCOMM = 0xe2 # begin common: name,NO_SECT,0,0
|
||||
N_ECOMM = 0xe4 # end common: name,n_sect,0,0
|
||||
N_ECOML = 0xe8 # end common (local name): 0,n_sect,0,address
|
||||
N_LENG = 0xfe # second stab entry with length information
|
||||
|
||||
|
||||
def dict_from_struct(with_bytes, using_struct, to_names):
|
||||
result = {}
|
||||
|
||||
# unpack
|
||||
values = using_struct.unpack(str(with_bytes))
|
||||
|
||||
# walk the unpacked values
|
||||
keys = to_names.split(" ")
|
||||
for i in range(0, len(keys)):
|
||||
result[keys[i]] = values[i]
|
||||
return result
|
||||
|
||||
|
||||
class File(object):
|
||||
"""toplevel object representing a mach-o file"""
|
||||
# struct mach_header
|
||||
# {
|
||||
# uint32_t magic;
|
||||
# cpu_type_t cputype;
|
||||
# cpu_subtype_t cpusubtype;
|
||||
# uint32_t filetype;
|
||||
# uint32_t ncmds;
|
||||
# uint32_t sizeofcmds;
|
||||
# uint32_t flags;
|
||||
# };
|
||||
# struct mach_header_64
|
||||
# {
|
||||
# uint32_t magic;
|
||||
# cpu_type_t cputype;
|
||||
# cpu_subtype_t cpusubtype;
|
||||
# uint32_t filetype;
|
||||
# uint32_t ncmds;
|
||||
# uint32_t sizeofcmds;
|
||||
# uint32_t flags;
|
||||
# uint32_t reserved;
|
||||
# };
|
||||
mach_header_magic32 = 0xfeedface
|
||||
mach_header_magic64 = 0xfeedfacf
|
||||
mach_header_f = "IiiIIII"
|
||||
mach_header_n = "magic cputype cpusubtype filetype ncmds sizeofcmds flags"
|
||||
|
||||
def __init__(self, file, debug=False):
|
||||
self._symtab = None
|
||||
self._stringtab = None
|
||||
self._segments = {}
|
||||
self._sections = {}
|
||||
self._section_index = 1
|
||||
self._uuid = None
|
||||
self._debug = debug
|
||||
|
||||
# stash the file and the header offset
|
||||
self._file = file
|
||||
|
||||
# read the file header
|
||||
self._properties = self.unpack(self.mach_header_f, self.mach_header_n)
|
||||
|
||||
# skip the reserved field in a 64-bit header
|
||||
if self['magic'] == self.mach_header_magic64:
|
||||
self._file.seek(4, io.SEEK_CUR)
|
||||
elif self['magic'] != self.mach_header_magic32:
|
||||
raise RuntimeError("bad magic number {:#010x} in mach-o header".format(self['magic']))
|
||||
|
||||
# read load commands
|
||||
for i in range(1, self['ncmds']):
|
||||
cmd = LoadCommand.from_file(self)
|
||||
|
||||
# reset the file pointer to the next load command
|
||||
# print "seek to load command at {} + {}".format(cmd['__offset'], cmd['cmdsize'])
|
||||
self._file.seek(cmd['__offset'] + cmd['cmdsize'])
|
||||
|
||||
def debug(self, str):
|
||||
if self._debug is not False:
|
||||
print str
|
||||
|
||||
# access to the UUID
|
||||
@property
|
||||
def uuid(self):
|
||||
return self._uuid
|
||||
|
||||
@uuid.setter
|
||||
def uuid(self, value):
|
||||
self._uuid = value
|
||||
|
||||
# access to the symbol table
|
||||
@property
|
||||
def symtab(self):
|
||||
return self._symtab
|
||||
|
||||
@symtab.setter
|
||||
def symtab(self, value):
|
||||
self._symtab = value
|
||||
|
||||
# access to the string table
|
||||
@property
|
||||
def stringtab(self):
|
||||
return self._stringtab
|
||||
|
||||
@stringtab.setter
|
||||
def stringtab(self, value):
|
||||
self._stringtab = value
|
||||
|
||||
# access to the segment list
|
||||
@property
|
||||
def segments(self):
|
||||
return self._segments
|
||||
|
||||
# segments call this to self-register
|
||||
def add_segment(self, segment):
|
||||
self.debug("segment " + segment['segname'])
|
||||
self._segments[segment['segname']] = segment
|
||||
|
||||
# access to the section list
|
||||
@property
|
||||
def sections(self):
|
||||
return self._sections
|
||||
|
||||
def sections_inorder(self):
|
||||
'''iterate sections in file order'''
|
||||
for i in range(1, self._section_index - 1):
|
||||
yield self._sections[i]
|
||||
|
||||
# sections call this to self-register
|
||||
def add_section(self, section):
|
||||
sectionname = "{},{}".format(section['segname'], section['sectname'])
|
||||
self.debug("section {}:{} @ {}".format(self._section_index, sectionname, section['offset']))
|
||||
self._sections[self._section_index] = section
|
||||
self._section_index += 1
|
||||
self._sections[sectionname] = section
|
||||
|
||||
# get the object file flavor (32/64)
|
||||
@property
|
||||
def flavor(self):
|
||||
if self['magic'] == self.mach_header_magic32:
|
||||
return 32
|
||||
return 64
|
||||
|
||||
# allow properties to be fetched by indexing us
|
||||
def __getitem__(self, index):
|
||||
if index in self._properties:
|
||||
return self._properties[index]
|
||||
else:
|
||||
return None
|
||||
|
||||
# unpack a structure from the file
|
||||
def unpack(self, format, names, offset=-1, rewind=False):
|
||||
|
||||
# if we are told to seek, do that now
|
||||
if offset != -1:
|
||||
self._file.seek(offset)
|
||||
|
||||
# remember where we read from
|
||||
pre_offset = self._file.tell()
|
||||
|
||||
# fetch the data and unpack it
|
||||
s = struct.Struct(format)
|
||||
bytes = self.readbytes(s.size, offset, rewind)
|
||||
result = dict_from_struct(bytes, s, names)
|
||||
|
||||
# default result dict entries
|
||||
result['__offset'] = pre_offset
|
||||
result['__dirty'] = False
|
||||
|
||||
# if we were asked to rewind
|
||||
if rewind is True:
|
||||
self._file.seek(pre_offset)
|
||||
|
||||
return result
|
||||
|
||||
# repack a structure into the file
|
||||
def repack(self, format, names, props):
|
||||
|
||||
# make a list of the property arguments for the format
|
||||
values = [props[key] for key in names.split()]
|
||||
s = struct.Struct(format)
|
||||
bytes = s.pack(*values)
|
||||
|
||||
self.writebytes(bytes, props['__offset'])
|
||||
|
||||
# read bytes from the file
|
||||
def readbytes(self, size, offset=-1, rewind=False):
|
||||
o_offset = self._file.tell()
|
||||
if offset != -1:
|
||||
self._file.seek(offset)
|
||||
bytes = self._file.read(size)
|
||||
if rewind is True:
|
||||
self._file.seek(o_offset)
|
||||
return bytearray(bytes)
|
||||
|
||||
# write bytes to the file
|
||||
def writebytes(self, bytes, offset=-1):
|
||||
if offset != -1:
|
||||
self._file.seek(offset)
|
||||
count = len(bytes)
|
||||
wrote = self._file.write(bytes)
|
||||
if wrote != count:
|
||||
raise RuntimeError("tried to write {} bytes, only wrote {} bytes".format(count, wrote))
|
||||
|
||||
# push any updates out to the file
|
||||
def update(self):
|
||||
updated = False
|
||||
for i in range(1, self._section_index):
|
||||
if self._sections[i].update():
|
||||
updated = True
|
||||
if updated:
|
||||
self._file.flush()
|
||||
return updated
|
||||
|
||||
# locate a segment by name
|
||||
def segment_for_name(self, name):
|
||||
if name in self._segments:
|
||||
return self._segments[name]
|
||||
return None
|
||||
|
||||
# locate the section that contains data for a given address range
|
||||
def section_for_address(self, addr):
|
||||
for sect in self._sections.itervalues():
|
||||
saddr = sect['addr']
|
||||
send = saddr + sect['size']
|
||||
if (saddr <= addr) and (send > addr):
|
||||
return sect
|
||||
return None
|
||||
|
||||
# locate a section by name
|
||||
def section_for_name(self, name):
|
||||
if name in self._sections:
|
||||
return self._sections[name]
|
||||
return None
|
||||
|
||||
# locate symbols in a section
|
||||
def symbols_in_section(self, name):
|
||||
matching = {'section': name}
|
||||
return self._symtab.matching(matching)
|
||||
|
||||
# locate symbols referencing an address
|
||||
def symbols_for_address(self, addr):
|
||||
matching = {'n_value': addr, 'n_type': N_ABS}
|
||||
return self._symtab.matching(matching)
|
||||
|
||||
|
||||
class LoadCommand(object):
|
||||
"""base class for load commands"""
|
||||
|
||||
# struct load_command
|
||||
# {
|
||||
# uint32_t cmd;
|
||||
# uint32_t cmdsize;
|
||||
# };
|
||||
lc_names = {
|
||||
0x00000001: "LC_SEGMENT",
|
||||
0x00000002: "LC_SYMTAB",
|
||||
0x00000019: "LC_SEGMENT_64",
|
||||
0x0000001b: "LC_UUID",
|
||||
0x00000026: "LC_FUNCTION_STARTS",
|
||||
0x00000029: "LC_DATA_IN_CODE",
|
||||
0x0000002A: "LC_SOURCE_VERSION",
|
||||
}
|
||||
load_command_f = "II"
|
||||
load_command_n = "cmd cmdsize"
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, parent):
|
||||
|
||||
# read the header and reset the file pointer so that a subclass
|
||||
# will get the whole thing
|
||||
properties = parent.unpack(cls.load_command_f, cls.load_command_n, rewind=True)
|
||||
|
||||
# hand off to the first subclass that is interested in it
|
||||
obj = None
|
||||
for subcls in cls.__subclasses__():
|
||||
obj = subcls.for_load_command(properties['cmd'], parent)
|
||||
if obj is not None:
|
||||
break
|
||||
|
||||
# if we didn't find a subclass, return a dummy so that it can be skipped
|
||||
if obj is None:
|
||||
# print "ignoring unhandled section {}".format(properties['cmd'])
|
||||
obj = LoadCommand(parent)
|
||||
|
||||
return obj
|
||||
|
||||
def __init__(self, parent):
|
||||
self._properties = parent.unpack(self.load_command_f, self.load_command_n)
|
||||
|
||||
# allow properties to be fetched by indexing us
|
||||
def __getitem__(self, index):
|
||||
if index in self._properties:
|
||||
return self._properties[index]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class Segment(LoadCommand):
|
||||
"""handles segment commands"""
|
||||
|
||||
# struct segment_command
|
||||
# {
|
||||
# uint32_t cmd;
|
||||
# uint32_t cmdsize;
|
||||
# char segname[16];
|
||||
# uint32_t vmaddr;
|
||||
# uint32_t vmsize;
|
||||
# uint32_t fileoff;
|
||||
# uint32_t filesize;
|
||||
# vm_prot_t maxprot;
|
||||
# vm_prot_t initprot;
|
||||
# uint32_t nsects;
|
||||
# uint32_t flags;
|
||||
# };
|
||||
# struct segment_command_64
|
||||
# {
|
||||
# uint32_t cmd;
|
||||
# uint32_t cmdsize;
|
||||
# char segname[16];
|
||||
# uint64_t vmaddr;
|
||||
# uint64_t vmsize;
|
||||
# uint64_t fileoff;
|
||||
# uint64_t filesize;
|
||||
# vm_prot_t maxprot;
|
||||
# vm_prot_t initprot;
|
||||
# uint32_t nsects;
|
||||
# uint32_t flags;
|
||||
# };
|
||||
segment_command32_f = "II16sIIIIiiII"
|
||||
segment_command64_f = "II16sQQQQiiII"
|
||||
segment_command_n = "cmd cmdsize segname vmaddr vmsize fileoff filesize maxprot initprot nsects flags"
|
||||
|
||||
@classmethod
|
||||
def for_load_command(cls, cmd, parent):
|
||||
if cmd in cls.lc_names:
|
||||
if cls.lc_names[cmd] == "LC_SEGMENT":
|
||||
return cls(cmd, parent, 32)
|
||||
if cls.lc_names[cmd] == "LC_SEGMENT_64":
|
||||
return cls(cmd, parent, 64)
|
||||
return None
|
||||
|
||||
def __init__(self, cmd, parent, flavor):
|
||||
if flavor == 32:
|
||||
self._properties = parent.unpack(self.segment_command32_f, self.segment_command_n)
|
||||
else:
|
||||
self._properties = parent.unpack(self.segment_command64_f, self.segment_command_n)
|
||||
|
||||
# strip NULs from the segment name
|
||||
self._properties['segname'] = self._properties['segname'].rstrip("\x00")
|
||||
|
||||
# advertise to the parent
|
||||
parent.add_segment(self)
|
||||
|
||||
# iterate sections
|
||||
for i in range(0, self['nsects']):
|
||||
Section(parent, flavor)
|
||||
|
||||
|
||||
class Section(object):
|
||||
"""base class for text/data sections"""
|
||||
|
||||
# struct section
|
||||
# {
|
||||
# char sectname[16];
|
||||
# char segname[16];
|
||||
# uint32_t addr;
|
||||
# uint32_t size;
|
||||
# uint32_t offset;
|
||||
# uint32_t align;
|
||||
# uint32_t reloff;
|
||||
# uint32_t nreloc;
|
||||
# uint32_t flags;
|
||||
# uint32_t reserved1; /* offset or index */
|
||||
# uint32_t reserved2; /* count or sizeof */
|
||||
# };
|
||||
# struct section_64
|
||||
# {
|
||||
# char sectname[16];
|
||||
# char segname[16];
|
||||
# uint64_t addr;
|
||||
# uint64_t size;
|
||||
# uint32_t offset;
|
||||
# uint32_t align;
|
||||
# uint32_t reloff;
|
||||
# uint32_t nreloc;
|
||||
# uint32_t flags;
|
||||
# uint32_t reserved1; /* offset or index */
|
||||
# uint32_t reserved2; /* count or sizeof */
|
||||
# uint32_t reserved3;
|
||||
# };
|
||||
section32_f = "16s16sIIIIIIIII"
|
||||
section64_f = "16s16sQQIIIIIIII"
|
||||
section_n = "sectname segname addr size offset align reloff nreloc flags index count"
|
||||
|
||||
def __init__(self, parent, flavor):
|
||||
if flavor == 32:
|
||||
self._properties = parent.unpack(self.section32_f, self.section_n)
|
||||
else:
|
||||
self._properties = parent.unpack(self.section64_f, self.section_n)
|
||||
|
||||
# strip NULs from the segment and section names
|
||||
self._properties['segname'] = self._properties['segname'].rstrip("\x00")
|
||||
self._properties['sectname'] = self._properties['sectname'].rstrip("\x00")
|
||||
|
||||
# give ourselves a canonical name
|
||||
self._properties['name'] = "{},{}".format(self['segname'], self['sectname'])
|
||||
|
||||
# register so that we can be found by either name or index
|
||||
self._parent = parent
|
||||
parent.add_section(self)
|
||||
|
||||
# allow properties to be fetched by indexing us
|
||||
# and for a slice to index the 'bytes' property
|
||||
def __getitem__(self, index):
|
||||
|
||||
if isinstance(index, slice):
|
||||
return self['bytes'][index]
|
||||
|
||||
if index not in self._properties:
|
||||
if index == 'bytes':
|
||||
# can't mess with bytes in a zerofill section
|
||||
if (self['flags'] & SECTION_TYPE) == S_ZEROFILL:
|
||||
return None
|
||||
# self._parent.debug("read {}/{}".format(self['offset'], self['size']))
|
||||
v = self._parent.readbytes(self['size'], offset=self['offset'])
|
||||
# print ord(v[0]), ord(v[1]), ord(v[2]), ord(v[3])
|
||||
elif index == 'end':
|
||||
v = self['addr'] + self['size'] - 1
|
||||
else:
|
||||
return None
|
||||
self._properties[index] = v
|
||||
return self._properties[index]
|
||||
|
||||
# allow some properties to be changed
|
||||
def __setitem__(self, index, value):
|
||||
if isinstance(index, slice):
|
||||
# prefetch the 'bytes' property
|
||||
value = self['bytes']
|
||||
self._properties['bytes'][index] = value
|
||||
|
||||
if index == 'bytes':
|
||||
if len(value) != self['size']:
|
||||
raise RuntimeError("wrong size for section data")
|
||||
self._properties['bytes'] = value
|
||||
self._properties['__dirty'] = True
|
||||
else:
|
||||
raise RuntimeError("section attribute '{}' is not writable", index)
|
||||
|
||||
# if we have been dirtied by e.g. variable patching, write our bytes back
|
||||
def update(self):
|
||||
if self['__dirty']:
|
||||
self._parent.writebytes(self['bytes'], self['offset'])
|
||||
self._properties['__dirty'] = False
|
||||
return True
|
||||
return False
|
||||
|
||||
class Symtab(LoadCommand):
|
||||
"""symbol table"""
|
||||
|
||||
# struct symtab_command
|
||||
# {
|
||||
# uint_32 cmd;
|
||||
# uint_32 cmdsize;
|
||||
# uint_32 symoff;
|
||||
# uint_32 nsyms;
|
||||
# uint_32 stroff;
|
||||
# uint_32 strsize;
|
||||
# };
|
||||
symtab_f = "IIIIII"
|
||||
symtab_n = "cmd cmdsize symoff nsyms stroff strsize"
|
||||
|
||||
@classmethod
|
||||
def for_load_command(cls, cmd, parent):
|
||||
if cmd in cls.lc_names:
|
||||
if cls.lc_names[cmd] in ("LC_SYMTAB"):
|
||||
return cls(parent)
|
||||
return None
|
||||
|
||||
def __init__(self, parent):
|
||||
self._nlist = []
|
||||
|
||||
# read the symtab header
|
||||
self._properties = parent.unpack(self.symtab_f, self.symtab_n)
|
||||
|
||||
# read the string table first
|
||||
parent.stringtab = StringTable(parent, self['stroff'], self['strsize'])
|
||||
|
||||
# read the name list
|
||||
self._nlist.append(Nlist(parent, self['symoff']))
|
||||
for n in range(2, self['nsyms']):
|
||||
self._nlist.append(Nlist(parent))
|
||||
|
||||
# print "symtab: {} symbols".format(len(self._nlist))
|
||||
|
||||
parent.symtab = self
|
||||
|
||||
# find symbols matching a dictionary
|
||||
def matching(self, matching_dict):
|
||||
result = []
|
||||
|
||||
# walk the symbol set looking for matches
|
||||
for sym in self._nlist:
|
||||
# print " {}:".format(sym['name'])
|
||||
|
||||
if sym.matches(matching_dict):
|
||||
result.append(sym)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class StringTable(object):
|
||||
"""a string table"""
|
||||
|
||||
_strings = {}
|
||||
|
||||
def __init__(self, parent, offset, size):
|
||||
# read the raw string table from the file
|
||||
bytes = parent.readbytes(size, offset=offset)
|
||||
|
||||
# parse out NUL-separate strings and index them
|
||||
index = 0
|
||||
ptr = -1
|
||||
s = ""
|
||||
for c in bytes:
|
||||
ptr += 1
|
||||
if c == 0:
|
||||
if len(s) > 0:
|
||||
# print "strtab: {} '{}'".format(index, s)
|
||||
self._strings[index] = s
|
||||
parent.debug("string {}:'{}'".format(index, s))
|
||||
s = ""
|
||||
else:
|
||||
if len(s) == 0:
|
||||
index = ptr;
|
||||
s += chr(c)
|
||||
|
||||
def __getitem__(self, index):
|
||||
if index in self._strings:
|
||||
return self._strings[index]
|
||||
elif index == 0:
|
||||
return ""
|
||||
else:
|
||||
return None
|
||||
|
||||
class UUIDCommand(LoadCommand):
|
||||
"""handles segment commands"""
|
||||
|
||||
# struct uuid_command
|
||||
# {
|
||||
# uint32_t cmd;
|
||||
# uint32_t cmdsize;
|
||||
# uint8_t uuid[16];
|
||||
# };
|
||||
uuid_command_f = "II16s"
|
||||
uuid_command_n = "cmd cmdsize uuid"
|
||||
|
||||
@classmethod
|
||||
def for_load_command(cls, cmd, parent):
|
||||
if cmd in cls.lc_names:
|
||||
if cls.lc_names[cmd] == "LC_UUID":
|
||||
return cls(cmd, parent)
|
||||
return None
|
||||
|
||||
def __init__(self, cmd, parent):
|
||||
self._properties = parent.unpack(self.uuid_command_f, self.uuid_command_n)
|
||||
|
||||
parent.debug('uuid: {}'.format(self._properties))
|
||||
|
||||
# advertise to the parent
|
||||
parent.uuid = self._properties['uuid']
|
||||
|
||||
class Nlist(object):
|
||||
"""a symbol table entry"""
|
||||
|
||||
# struct nlist
|
||||
# {
|
||||
# union {
|
||||
# int32_t n_strx; /* string table offset */
|
||||
# } n_un;
|
||||
# uint8_t n_type; /* symbol type */
|
||||
# uint8_t n_sect; /* section index */
|
||||
# int16_t n_desc; /* debug description */
|
||||
# uint32_t n_value; /* address/value */
|
||||
# };
|
||||
# struct nlist_64
|
||||
# {
|
||||
# union {
|
||||
# uint32_t n_strx;
|
||||
# } n_un;
|
||||
# uint8_t n_type;
|
||||
# uint8_t n_sect;
|
||||
# uint16_t n_desc;
|
||||
# uint64_t n_value;
|
||||
# };
|
||||
nlist32_f = "IBBHI"
|
||||
nlist64_f = "IBBHQ"
|
||||
nlist_n = "n_strx n_type n_sect n_desc n_value"
|
||||
|
||||
def __init__(self, parent, offset=-1):
|
||||
if parent.flavor == 32:
|
||||
self._properties = parent.unpack(self.nlist32_f, self.nlist_n, offset=offset)
|
||||
elif parent.flavor == 64:
|
||||
self._properties = parent.unpack(self.nlist64_f, self.nlist_n, offset=offset)
|
||||
else:
|
||||
raise RuntimeError("bad nlist flavor")
|
||||
|
||||
# save a reference to the parent, we need it for lazy name evaluation
|
||||
self._parent = parent
|
||||
self['name']
|
||||
parent.debug("symbol {}".format(self._properties))
|
||||
|
||||
# match the symbol against a dictionary of same
|
||||
def matches(self, matching_dict):
|
||||
|
||||
# walk the matching dictionary
|
||||
for key in matching_dict:
|
||||
|
||||
# fetch the symbol's value for this key; if we don't
|
||||
# have a value we cannot match
|
||||
v = self[key]
|
||||
if v is None:
|
||||
return False
|
||||
c = matching_dict[key]
|
||||
|
||||
# simple integer match?
|
||||
if isinstance(c, int):
|
||||
if c != v:
|
||||
return False
|
||||
continue
|
||||
|
||||
# integer masking match?
|
||||
if isinstance(c, tuple) and (len(c) == 3):
|
||||
mask, require, prohibit = c
|
||||
v &= mask
|
||||
if (v & prohibit) != 0:
|
||||
return False
|
||||
if (v & require) != require:
|
||||
return False
|
||||
continue
|
||||
|
||||
# string match?
|
||||
if isinstance(c, str):
|
||||
r = re.compile(c)
|
||||
m = r.match(v)
|
||||
if m is None:
|
||||
return False
|
||||
continue
|
||||
|
||||
# don't actually know how to handle this...
|
||||
raise RuntimeError("don't know how to match '{}' using '{}'".format(key, v))
|
||||
|
||||
return True
|
||||
|
||||
# Allow properties to be fetched by indexing us
|
||||
#
|
||||
# As some properties aren't known as parse time (e.g. section names)
|
||||
# we look them up lazily.
|
||||
#
|
||||
def __getitem__(self, index):
|
||||
if index not in self._properties:
|
||||
v = None
|
||||
if index == 'name':
|
||||
v = self._parent.stringtab[self['n_strx']]
|
||||
elif index == 'sectname':
|
||||
i = self['n_sect']
|
||||
if i not in self._parent.sections:
|
||||
return None
|
||||
v = self._parent.sections[i]['name']
|
||||
elif index == 'section':
|
||||
stype = self._properties['n_type'] & N_TYPE
|
||||
if stype == N_ABS or stype == N_DATA:
|
||||
v = self._parent.section_for_address(self['n_value'])
|
||||
elif stype == N_SECT:
|
||||
v = self._parent.sections[self['n_sect']]
|
||||
self._properties[index] = v
|
||||
return self._properties[index]
|
||||
|
||||
# Read from whatever the symbol points to
|
||||
def unpack(self, format, names):
|
||||
|
||||
# look up the section we're operating on
|
||||
sect = self['section']
|
||||
addr = self['n_value']
|
||||
offset = addr - sect['addr']
|
||||
# self._parent.debug("addr 0x{:x} section addr 0x{:x} offset 0x{:x}".format(addr, sect['addr'], offset))
|
||||
bytes = sect['bytes']
|
||||
|
||||
if format == 'C': # Non-standard C String format
|
||||
chunk = bytes[offset:bytes.index('\0', offset)]
|
||||
s = struct.Struct('%ds' % len(chunk))
|
||||
else:
|
||||
# slice the requested range and decode
|
||||
s = struct.Struct(format)
|
||||
# print "{}:{}".format(offset, offset + s.size)
|
||||
chunk = bytes[offset:(offset + s.size)]
|
||||
|
||||
return dict_from_struct(chunk, s, names)
|
||||
|
||||
# write to whatever the symbol points to
|
||||
#
|
||||
# XXX needs to be updated to take a dict as argument
|
||||
def pack(self, format, values):
|
||||
|
||||
# look up the section we're operating on
|
||||
sect = self['section']
|
||||
addr = self['n_value']
|
||||
offset = addr - sect['addr']
|
||||
|
||||
# encode the values
|
||||
self._parent.debug("packing {}".format(values))
|
||||
s = struct.Struct(format)
|
||||
chunk = s.pack(*values)
|
||||
|
||||
# and update the section
|
||||
bytes = sect['bytes']
|
||||
bytes[offset:(offset + s.size)] = chunk
|
||||
sect['bytes'] = bytes
|
||||
|
||||
# dereference the symbol and get a single value it points to
|
||||
def deref(self, format):
|
||||
contents = self.unpack(format, 'value')
|
||||
return contents['value']
|
||||
|
||||
# assign a value to the thing the symbol references
|
||||
def assign(self, format, value):
|
||||
self.pack(format, [value])
|
||||
|
||||
|
||||
def to_binary(macho, output, verbose=False):
|
||||
'''emit a compact in-memory binary representation of the mach-o file'''
|
||||
|
||||
output.seek(0)
|
||||
data_offset = 0
|
||||
for section in macho.sections_inorder():
|
||||
|
||||
# We assume that we simply want to pack all of the
|
||||
# text/data sections in the input file into the output, and
|
||||
# that we should stop once we hit a zero-filled section because
|
||||
# we've run out of data.
|
||||
|
||||
if (((section['flags'] & S_ZEROFILL) != 0) or
|
||||
(section['sectname'] == '__common') or
|
||||
(section['sectname'] == '__bss')):
|
||||
break
|
||||
|
||||
# Make a note of where we are in the output file when
|
||||
# we start processing sections in the __DATA segment
|
||||
if data_offset == 0 and section['segname'] == '__DATA':
|
||||
data_offset = output.tell()
|
||||
|
||||
# pad sections to maintain alignment
|
||||
alignment = 1 << section['align']
|
||||
if section['segname'] == '__DATA':
|
||||
skew = data_offset
|
||||
else:
|
||||
skew = 0
|
||||
padding = (alignment - ((output.tell() - skew) % alignment)) % alignment
|
||||
|
||||
zeros = bytearray.fromhex('00' * padding)
|
||||
output.write(zeros)
|
||||
|
||||
# write the section contents
|
||||
if section['size'] > 0:
|
||||
if verbose is True:
|
||||
print '{}/{} 0x{:x}/0x{:x} @ 0x{:x}'.format(section['segname'],
|
||||
section['sectname'],
|
||||
section['addr'],
|
||||
section['size'],
|
||||
output.tell())
|
||||
output.write(section['bytes'])
|
||||
|
||||
|
||||
#
|
||||
# Be a useful utility.
|
||||
#
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='mach-o utility')
|
||||
parser.add_argument('-m', '--macho', required=True,
|
||||
help='path to the mach-o file')
|
||||
parser.add_argument('-o', '--output',
|
||||
help='path to the output file')
|
||||
parser.add_argument('-c', '--convert', dest='format', choices=['binary', 'TBD'],
|
||||
help='convert to the specified format')
|
||||
parser.add_argument('-v', '--verbose', default=False, action='store_true',
|
||||
help='enable verbose output')
|
||||
parser.add_argument('-d', '--debug', default=False, action='store_true',
|
||||
help='enable debug output')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
f = File(io.open(args.macho, 'rb'), args.debug)
|
||||
except:
|
||||
print "macho: failed opening " + args.macho
|
||||
raise
|
||||
|
||||
if args.format == 'binary' or args.format == 'objcopy':
|
||||
if args.output is None:
|
||||
raise RuntimeError("missing -o argument to specify output name")
|
||||
o = io.open(args.output, 'wb')
|
||||
|
||||
to_binary(f, o, args.verbose)
|
||||
o.close()
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2013-2015 Apple Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
#
|
||||
# RTXC Mach-o post processing
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
from argparse import ArgumentParser
|
||||
import macho
|
||||
import io
|
||||
from struct import Struct
|
||||
import subprocess
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
def memory_slices(data, size):
|
||||
m = memoryview(data)
|
||||
|
||||
for index in xrange(0, len(data), size):
|
||||
yield m[index:index+size]
|
||||
|
||||
|
||||
def hexdump(bytes, stride=16):
|
||||
for i, data in enumerate(memory_slices(bytes, stride)):
|
||||
dataString = ' '.join(map('{:02X}'.format, data.tolist()))
|
||||
print('{:04x}: {}'.format(i * stride, dataString))
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
def magicToInt(string, format='<L'):
|
||||
return Struct(format).unpack(string)[0]
|
||||
|
||||
class RTXCId(namedtuple('RTXCId', ['rtxc_version',
|
||||
'client_version',
|
||||
'uuid'])):
|
||||
STRUCT = Struct('<4L16s')
|
||||
MAGIC = magicToInt('uuid')
|
||||
VERSION = 0x01
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self.STRUCT.size
|
||||
|
||||
@property
|
||||
def bytes(self):
|
||||
return self.STRUCT.pack(self.MAGIC,
|
||||
self.VERSION,
|
||||
self.rtxc_version,
|
||||
self.client_version,
|
||||
self.uuid.bytes)
|
||||
|
||||
def get_symbol_addr(mf, name):
|
||||
symbol_match = mf.symtab.matching({'name': name})
|
||||
if symbol_match:
|
||||
return symbol_match[0]['n_value']
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def patch_uuid(mf, file_path, build_tag, verbose=False):
|
||||
'''
|
||||
Patch the UUID with the value provided by the linker
|
||||
'''
|
||||
uuid_address = get_symbol_addr(mf, '_UUID')
|
||||
if uuid_address is None:
|
||||
print('WARNING: No UUID symbol found. will not patch {}'.format(file_path))
|
||||
elif verbose:
|
||||
print('Found UUID container at address 0x{:x}'.format(uuid_address))
|
||||
|
||||
tag_address = get_symbol_addr(mf, '_build_tag_string_contents')
|
||||
if tag_address is None:
|
||||
print('WARNING: No build tag string symbol found. will not patch {}'.format(file_path))
|
||||
elif verbose:
|
||||
print('Found build tag string container at address 0x{:x}'.format(tag_address))
|
||||
|
||||
text_section = mf.section_for_name('__TEXT,__text')
|
||||
|
||||
if verbose:
|
||||
print('Found text section at address 0x{:x}'.format(text_section['addr']))
|
||||
|
||||
uuid = UUID(bytes=mf.uuid)
|
||||
|
||||
if verbose:
|
||||
print('Identified UUID: {}'.format(uuid))
|
||||
|
||||
# We're using the RTXC ID format used by other firmware teams, but setting
|
||||
# the version numbers to 0
|
||||
rtxcid = RTXCId(0,
|
||||
0,
|
||||
uuid)
|
||||
|
||||
if verbose:
|
||||
print('Full ID structure:')
|
||||
hexdump(rtxcid.bytes)
|
||||
|
||||
data = text_section['bytes']
|
||||
|
||||
if uuid_address is not None:
|
||||
data_patch_addr = uuid_address - text_section['addr']
|
||||
data[data_patch_addr:data_patch_addr+rtxcid.size] = rtxcid.bytes
|
||||
|
||||
if tag_address is not None and build_tag is not None:
|
||||
data_patch_addr = tag_address - text_section['addr']
|
||||
data[data_patch_addr:data_patch_addr+len(build_tag)] = build_tag
|
||||
|
||||
text_section['bytes'] = data
|
||||
|
||||
mf.update()
|
||||
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser(description="RTXC post processing for Mach-o files")
|
||||
parser.add_argument('-v', '--verbose',
|
||||
action='store_true')
|
||||
parser.add_argument('--build-tag',
|
||||
default=None,
|
||||
help="Name of the XBS build tag or local build tag")
|
||||
parser.add_argument('file',
|
||||
help="Mach-o file to operate on")
|
||||
args = parser.parse_args()
|
||||
|
||||
with io.open(args.file, "r+b") as f:
|
||||
mf = macho.File(f, debug=False)
|
||||
patch_uuid(mf, args.file, args.build_tag, args.verbose)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/tclsh
|
||||
#########################################################################################
|
||||
# Parse output from ld_classic that complains about missing symbols and look them
|
||||
# up in the named object file
|
||||
#
|
||||
|
||||
proc parse_line {line} {
|
||||
|
||||
global last_fname
|
||||
|
||||
set toks [split $line]
|
||||
if { ![regexp "ld_classic:" [lindex $toks 0]] } {
|
||||
puts "ignoring garbage '$line'"
|
||||
return
|
||||
}
|
||||
|
||||
set fname [lindex $toks 1]
|
||||
set relent [lindex $toks 7]
|
||||
set sect [lindex $toks 10]
|
||||
|
||||
# strip archive member name if present
|
||||
set ppos [string last "(" $fname];
|
||||
if {$ppos != -1} {
|
||||
set fname [string range $fname 0 [expr $ppos - 1]];
|
||||
}
|
||||
|
||||
#puts "% $fname % $relent % $sect"
|
||||
|
||||
set relname [find_sym $fname $relent $sect]
|
||||
|
||||
if {$last_fname != $fname} {
|
||||
puts "$fname:"
|
||||
set last_fname $fname
|
||||
}
|
||||
|
||||
puts " $relname"
|
||||
}
|
||||
|
||||
proc find_sym {fname relent sect} {
|
||||
|
||||
global reloc_$fname
|
||||
|
||||
if {![array exists reloc_$fname]} {
|
||||
get_relocs $fname
|
||||
}
|
||||
upvar 0 reloc_$fname relocs
|
||||
if {[array names relocs -exact $sect:$relent] == ""} {
|
||||
puts "Don't know reloc $relent for $fname"
|
||||
exit 1
|
||||
}
|
||||
return $relocs($sect:$relent)
|
||||
}
|
||||
|
||||
proc get_relocs {fname} {
|
||||
|
||||
global reloc_$fname
|
||||
upvar 0 reloc_$fname relocs
|
||||
|
||||
set body [exec otool -rv $fname]
|
||||
set lines [split $body "\n"]
|
||||
|
||||
set curr_sect "none"
|
||||
|
||||
foreach line $lines {
|
||||
|
||||
set toks [split [collapse $line]]
|
||||
|
||||
if {[lindex $toks 0] == "Relocation"} {
|
||||
set sect [lindex $toks 2]
|
||||
set count [lindex $toks 3]
|
||||
set curr_sect $sect
|
||||
set counter 0
|
||||
continue
|
||||
}
|
||||
|
||||
if {[string is xdigit [lindex $toks 0]]} {
|
||||
if {[llength $toks] == 7} {
|
||||
set relocs($curr_sect:$counter) [lindex $toks 6]
|
||||
}
|
||||
incr counter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc collapse {str} {
|
||||
|
||||
set before ""
|
||||
set after $str
|
||||
|
||||
while {$before != $after} {
|
||||
set before $after
|
||||
|
||||
set after [string map [list " " " "] $before]
|
||||
}
|
||||
return $after
|
||||
}
|
||||
|
||||
# Suck from standard input
|
||||
set last_fname ""
|
||||
while {[gets stdin line] > 0} {
|
||||
parse_line $line
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
def ofile_section_info(path):
|
||||
sections = []
|
||||
otool_output = subprocess.Popen(["otool", "-l", path], stdout=subprocess.PIPE).communicate()[0]
|
||||
for section_text in string.split(otool_output, "Section\n")[1:]:
|
||||
section = {}
|
||||
for field_text in string.split(section_text, "\n"):
|
||||
field_list = string.rsplit(string.strip(field_text))
|
||||
if len(field_list) == 2:
|
||||
section[field_list[0]] = field_list[1]
|
||||
sections.append(section)
|
||||
return sections
|
||||
|
||||
def ofile_cstring_size(path):
|
||||
sections = ofile_section_info(path)
|
||||
size = "0x00000000"
|
||||
for section in sections:
|
||||
if section['sectname'] == "__cstring" and section['segname'] == "__TEXT":
|
||||
size = section['size']
|
||||
pair = (size, path)
|
||||
return pair
|
||||
|
||||
def is_ofile(path):
|
||||
return os.path.isfile(path) and os.path.splitext(path)[1] == ".o"
|
||||
|
||||
def dir_visitor(size_path_pairs, dir_path, names):
|
||||
size_path_pairs.extend(map(ofile_cstring_size,
|
||||
filter(is_ofile,
|
||||
map(lambda n: os.path.join(dir_path, n), names))))
|
||||
|
||||
def main(paths):
|
||||
size_path_pairs = []
|
||||
for path in paths:
|
||||
if os.path.isdir(path):
|
||||
os.path.walk(path, dir_visitor, size_path_pairs)
|
||||
elif is_ofile(path):
|
||||
size_path_pairs.append(ofile_cstring_size(path))
|
||||
for (ofile_size, ofile_path) in size_path_pairs:
|
||||
sys.stdout.write("%s\t%s\n" % (ofile_size, ofile_path));
|
||||
|
||||
def usage():
|
||||
sys.stdout.write("%s path(s)...\n\n" % os.path.basename(sys.argv[0]))
|
||||
sys.stdout.write("\tReport the cstring section sizes for each .o"
|
||||
" file specified and for all .o files recursively"
|
||||
" found under each specified directory.\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
usage()
|
||||
else:
|
||||
main(sys.argv[1:])
|
||||
@@ -0,0 +1,893 @@
|
||||
#!/usr/bin/python
|
||||
import argparse
|
||||
import collections
|
||||
import csv
|
||||
import pipes
|
||||
import sys
|
||||
|
||||
try:
|
||||
import openpyxl
|
||||
except ImportError:
|
||||
openpyxl = None
|
||||
|
||||
class ParseError(Exception): pass
|
||||
|
||||
'''
|
||||
Representation of one pin in the GPIO config
|
||||
'''
|
||||
class BasePin(object):
|
||||
default_drive = 'X1'
|
||||
valid_pupdn = set(["PULL_UP", "PULL_DOWN", ""])
|
||||
valid_drive_strength = set()
|
||||
def __init__(self, gpio_pin_number):
|
||||
self.gpio_pin_number = int(gpio_pin_number)
|
||||
self.bump_name = None
|
||||
self.net_name = None
|
||||
self.function = None
|
||||
self.drive_strength = 'X1'
|
||||
self.pupd = None
|
||||
self.bus_hold = None
|
||||
self.input_mode = None
|
||||
self.row_number = None
|
||||
self.slew_rate = ''
|
||||
self.rom = None
|
||||
|
||||
def to_pinconfig_line(self):
|
||||
if None in (self.bump_name, self.net_name, self.function, self.drive_strength, self.pupd):
|
||||
raise ParseError, "Some attributes were unset for pin %d: %s" % (self.gpio_pin_number, (self.bump_name, self.net_name, self.function, self.drive_strength, self.pupd))
|
||||
|
||||
if self.drive_strength == self.default_drive:
|
||||
drive_strength = ''
|
||||
else:
|
||||
drive_strength = "DRIVE_{}".format(self.drive_strength)
|
||||
|
||||
if self.slew_rate == '':
|
||||
slew_rate = ''
|
||||
else:
|
||||
slew_rate = "{}_SLEW".format(self.slew_rate)
|
||||
|
||||
non_empty_props = [i for i in [self.function, self.pupd, drive_strength, slew_rate, self.bus_hold, self.input_mode] if i]
|
||||
args = " | ".join(non_empty_props) + ","
|
||||
return [args, "// {:>3} : {}".format(self.gpio_pin_number, self.bump_name), "-> " + self.net_name]
|
||||
|
||||
def set_function(self, fn):
|
||||
cfg_map = { 'in': 'CFG_IN',
|
||||
'input': 'CFG_IN',
|
||||
'out0': 'CFG_OUT_0',
|
||||
'out 0': 'CFG_OUT_0',
|
||||
'out1': 'CFG_OUT_1',
|
||||
'out 1': 'CFG_OUT_1',
|
||||
'alt func 0': 'CFG_FUNC0',
|
||||
'alt func 1': 'CFG_FUNC1',
|
||||
'alt func 2': 'CFG_FUNC2',
|
||||
'int rising': 'CFG_IN',
|
||||
'int falling': 'CFG_IN',
|
||||
'int any': 'CFG_IN',
|
||||
'disable': 'CFG_DISABLED',
|
||||
'disabled': 'CFG_DISABLED',
|
||||
# The numbering offset isn't a typo, these are legacy
|
||||
'function 1': 'CFG_FUNC0',
|
||||
'function 2': 'CFG_FUNC1',
|
||||
'function 3': 'CFG_FUNC2',
|
||||
}
|
||||
self.function = cfg_map[fn.lower()]
|
||||
|
||||
def set_net_name(self, name):
|
||||
self.net_name = name.strip()
|
||||
|
||||
def set_bump_name(self, name):
|
||||
self.bump_name = name.strip()
|
||||
|
||||
def set_drive_strength(self, strength):
|
||||
if strength == '':
|
||||
strength = self.default_drive
|
||||
else:
|
||||
strength = strength.upper()
|
||||
|
||||
if strength.isdigit():
|
||||
strength = self.drive_strength_prefix + strength
|
||||
|
||||
if not strength in self.valid_drive_strength:
|
||||
sorted_drive_strengths = sorted(self.valid_drive_strength, key=lambda item: (int(item[1:])))
|
||||
raise ParseError, "drive strength '{}' not valid (options are {})".format(strength.upper(), sorted_drive_strengths)
|
||||
self.drive_strength = strength
|
||||
|
||||
def set_pupd(self, pupd):
|
||||
pupd_map = { 'pullup strong' : 'PULL_UP_STRONG',
|
||||
'spu' : 'PULL_UP_STRONG',
|
||||
'pullup': 'PULL_UP',
|
||||
'pu': 'PULL_UP',
|
||||
'pulldown': 'PULL_DOWN',
|
||||
'pd': 'PULL_DOWN',
|
||||
'disable': '',
|
||||
'': ''}
|
||||
if not pupd_map[pupd.lower()] in self.valid_pupdn:
|
||||
pretty_pupdn = filter(lambda x : pupd_map[x] in self.valid_pupdn , pupd_map.keys())
|
||||
raise ParseError, "pu/pd value '{}' not valid (options are {})".format(pupd, pretty_pupdn)
|
||||
self.pupd = pupd_map[pupd.lower()]
|
||||
|
||||
|
||||
class PinV1(BasePin):
|
||||
valid_drive_strength = set(["X1", "X2", "X3", "X4"])
|
||||
drive_strength_prefix = 'X'
|
||||
|
||||
def set_bus_hold(self, hold):
|
||||
bus_hold_map = { '': '', '0': '', None: '', 'y': 'BUS_HOLD', 'yes': 'BUS_HOLD', '1': 'BUS_HOLD'}
|
||||
self.bus_hold = bus_hold_map[hold.lower()]
|
||||
|
||||
class PinV2(BasePin):
|
||||
valid_drive_strength = set(["X1", "X2", "X4", "X6"])
|
||||
drive_strength_prefix = 'X'
|
||||
|
||||
def set_bus_hold(self, hold):
|
||||
bus_hold_map = { '': '', '0': '', None: '', 'y': 'BUS_HOLD', 'yes': 'BUS_HOLD', '1': 'BUS_HOLD'}
|
||||
self.bus_hold = bus_hold_map[hold.lower()]
|
||||
|
||||
class PinV3(BasePin):
|
||||
valid_drive_strength = set(["X1", "X2", "X4", "X6"])
|
||||
drive_strength_prefix = 'X'
|
||||
|
||||
def set_slew_rate(self, slew):
|
||||
slew_map = { '': '', None: '', "fast": "FAST", "slow": "SLOW" }
|
||||
self.slew_rate = slew_map[slew.lower()]
|
||||
|
||||
class PinV4(PinV3):
|
||||
def set_input_mode(self, mode):
|
||||
mode_map = { '': '', None: '', 'cmos': '', 'schmitt': 'INPUT_SCHMITT', 'Schmitt': 'INPUT_SCHMITT'}
|
||||
self.input_mode = mode_map[mode]
|
||||
|
||||
class PinV5(PinV4):
|
||||
default_drive = 'S0'
|
||||
valid_drive_strength = set(["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10", "S11", "S12", "S13", "S14", "S15"])
|
||||
drive_strength_prefix = 'S'
|
||||
valid_pupdn = set(["PULL_UP_STRONG", "PULL_UP", "PULL_DOWN", ""])
|
||||
|
||||
def set_slew_rate(self, slew):
|
||||
slew_map = { '': '', None: '', "fast": "FAST", "slow": "SLOW", "very fast": "VERY_FAST" }
|
||||
self.slew_rate = slew_map[slew.lower()]
|
||||
|
||||
|
||||
class ReservedPin(object):
|
||||
def to_pinconfig_line(self):
|
||||
return ["CFG_DISABLED,"]
|
||||
|
||||
class GPIODomainInfo(object):
|
||||
def __init__(self, offset, count, reserved):
|
||||
self.id_offset = offset
|
||||
self.pin_count = count
|
||||
self.reserved_pins = reserved
|
||||
|
||||
class GPIODomain(object):
|
||||
def __init__(self, info, pin_class):
|
||||
self.id_offset = info.id_offset
|
||||
self.pin_count = info.pin_count
|
||||
self.reserved_pins = info.reserved_pins
|
||||
|
||||
# GPIO domains must start and end on 8-pin boundaries
|
||||
assert self.id_offset % 8 == 0
|
||||
if self.pin_count % 8 != 0:
|
||||
new_pin_count = self.pin_count + 8 - self.pin_count % 8
|
||||
self.reserved_pins |= set(range(self.id_offset + self.pin_count, self.id_offset + new_pin_count))
|
||||
self.pin_count = new_pin_count
|
||||
|
||||
self.pins = [None] * self.pin_count
|
||||
|
||||
for i in range(self.pin_count):
|
||||
if not i + self.id_offset in self.reserved_pins:
|
||||
pin = pin_class(i)
|
||||
pin.set_bump_name("UNSPECIFIED")
|
||||
pin.set_net_name("UNSPECIFIED")
|
||||
pin.set_function("disable")
|
||||
pin.set_pupd("disable")
|
||||
pin.set_drive_strength('')
|
||||
else:
|
||||
pin = ReservedPin()
|
||||
self.pins[i] = pin
|
||||
|
||||
def has_pin_id(self, pin_id):
|
||||
return pin_id >= self.id_offset and pin_id < self.id_offset + self.pin_count
|
||||
|
||||
def get_pin(self, pin_id):
|
||||
if not self.has_pin_id(pin_id):
|
||||
raise IndexError
|
||||
return self.pins[pin_id - self.id_offset]
|
||||
|
||||
class BasePinConfig(object):
|
||||
# Should be overridden in subclasses to a subclass of BasePin
|
||||
pin_class = None
|
||||
domain_info = None
|
||||
|
||||
def __init__(self):
|
||||
self.domains = [GPIODomain(info, self.pin_class) for info in self.domain_info]
|
||||
|
||||
def get_pin(self, pin_id):
|
||||
for d in self.domains:
|
||||
if d.has_pin_id(pin_id):
|
||||
return d.get_pin(pin_id)
|
||||
raise IndexError
|
||||
|
||||
def array_declaration(self, array_type, name, domain_index):
|
||||
if domain_index == 0:
|
||||
count_label = ''
|
||||
else:
|
||||
count_label = '_{}'.format(domain_index)
|
||||
templ = 'static const {} {}_{}[GPIO{}_GROUP_COUNT * GPIOPADPINS] = {{'
|
||||
return templ.format(array_type, name, domain_index, count_label)
|
||||
|
||||
def to_pinconfig_struct(self, name, array_type, max_domains):
|
||||
lines = []
|
||||
|
||||
for domain_idx in range(min(len(self.domains), max_domains)):
|
||||
domain = self.domains[domain_idx]
|
||||
|
||||
pin_matrix = [domain.pins[i].to_pinconfig_line() for i in range(domain.pin_count)]
|
||||
pin_matrix = tab_align(pin_matrix, min_column_tabs={0: 8})
|
||||
|
||||
lines.append(self.array_declaration(array_type, name, domain_idx))
|
||||
|
||||
rownum = 0
|
||||
for row in pin_matrix:
|
||||
if (rownum % 8) == 0:
|
||||
lines.append("\n/* Port {:2} */".format(rownum/8))
|
||||
lines.append("\t"+"".join(row))
|
||||
rownum += 1
|
||||
lines.append("};")
|
||||
lines.append('')
|
||||
return "\n".join(lines)
|
||||
|
||||
class BasePinConfigV1(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV1
|
||||
|
||||
class BasePinConfigV2(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV2
|
||||
|
||||
class BasePinConfigV3(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV3
|
||||
|
||||
class BasePinConfigV4(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV4
|
||||
|
||||
class BasePinConfigV5(BasePinConfig):
|
||||
array_type = 'uint32_t'
|
||||
pin_class = PinV5
|
||||
|
||||
class H4APinConfig(BasePinConfigV1):
|
||||
domain_info = [GPIODomainInfo(offset=0, count=208, reserved=set())]
|
||||
|
||||
class H5PPinConfig(BasePinConfigV2):
|
||||
domain_info = [GPIODomainInfo(offset=0, count=232, reserved=set())]
|
||||
|
||||
class H5GPinConfig(BasePinConfigV2):
|
||||
reserved_pins = set(range(14, 32) + range(140, 160) + range(212, 224) + range(241, 248))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=248, reserved=reserved_pins)]
|
||||
|
||||
class AlcatrazPinConfig(BasePinConfigV3):
|
||||
reserved_pins = set(range(24, 32) + range(45, 64) + range(151, 160) + range(173, 192))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=195, reserved=reserved_pins)]
|
||||
|
||||
class FijiPinConfig(BasePinConfigV4):
|
||||
reserved_pins = set(range(16, 32) + range(88, 96) + range(140, 160))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=204, reserved=reserved_pins)]
|
||||
|
||||
class CapriPinConfig(BasePinConfigV4):
|
||||
reserved_pins = set(range(16, 32))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=184, reserved=reserved_pins)]
|
||||
|
||||
class M7PinConfig(BasePinConfigV4):
|
||||
reserved_pins = set(range(10, 32))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=138, reserved=reserved_pins),
|
||||
GPIODomainInfo(offset=160, count=50, reserved=set())]
|
||||
|
||||
class MauiPinConfig(BasePinConfigV4):
|
||||
reserved_pins = set(range(55, 64) + range(92, 96) + range(152, 160) + range(168, 192))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=199, reserved=reserved_pins),
|
||||
GPIODomainInfo(offset=288, count=28, reserved=set())]
|
||||
|
||||
class ElbaPinConfig(BasePinConfigV5):
|
||||
reserved_pins = set(range(37, 64) + range(125, 128) + range(142, 160))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=220, reserved=reserved_pins),
|
||||
GPIODomainInfo(offset=288, count=28, reserved=set())]
|
||||
|
||||
class CaymanPinConfig(BasePinConfigV5):
|
||||
ap_reserved_pins = set(range(55, 64) + range(92, 96) + range(104, 128) + range(155, 160) + range(188, 192))
|
||||
aop_reserved_pins = set(range(318, 320) + range(330, 352))
|
||||
domain_info = [GPIODomainInfo(offset=0, count=208, reserved=ap_reserved_pins), # AP
|
||||
GPIODomainInfo(offset=288, count=85, reserved=aop_reserved_pins), # AOP
|
||||
GPIODomainInfo(offset=256, count=7, reserved=set())] # SEP
|
||||
output_domains = 2
|
||||
|
||||
class M8PinConfig(BasePinConfigV5):
|
||||
ap_reserved_pins = set(range(138, 160))
|
||||
aop_reserved_pins = set(range(264, 288))
|
||||
domain_info = [GPIODomainInfo(offset=32, count=139, reserved=ap_reserved_pins), # AP
|
||||
GPIODomainInfo(offset=256, count=96, reserved=aop_reserved_pins), # AOP
|
||||
GPIODomainInfo(offset=0, count=7, reserved=set()),
|
||||
GPIODomainInfo(offset=192, count=96, reserved=set())]
|
||||
output_domains = 2
|
||||
|
||||
def excel_reader(filename, sheet):
|
||||
try:
|
||||
workbook = openpyxl.load_workbook(filename, data_only=True)
|
||||
except IOError:
|
||||
sys.stderr.write('Could not open workbook {}\n'.format(filename))
|
||||
return None
|
||||
try:
|
||||
worksheet = workbook.get_sheet_by_name(sheet)
|
||||
except KeyError:
|
||||
sys.stderr.write('Could not open worksheet {}\n'.format(sheet))
|
||||
return None
|
||||
|
||||
def row_generator(ws):
|
||||
def cell_to_str(cell):
|
||||
value = cell.value
|
||||
if value is None:
|
||||
value = ''
|
||||
return unicode(value)
|
||||
|
||||
for row in ws.rows:
|
||||
yield [cell_to_str(cell) for cell in row]
|
||||
|
||||
return row_generator(worksheet)
|
||||
|
||||
def try_parse_hex(i):
|
||||
try:
|
||||
return int(i, 16)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def row_is_empty(row):
|
||||
for col in row:
|
||||
if not col is None and len(col.strip()) > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def row_is_footer(row):
|
||||
for col in row:
|
||||
if not col is None:
|
||||
stripped = col.strip()
|
||||
if len(stripped) > 0 and stripped != '--':
|
||||
return False
|
||||
return True
|
||||
|
||||
class CSVParser(object):
|
||||
def __init__(self, pinconfig_class, per_target_headers):
|
||||
self.pinconfig_class = pinconfig_class
|
||||
self.per_target_headers = per_target_headers
|
||||
self.targets = per_target_headers.keys()
|
||||
# Map the standardized name for a property to a list of acceptable names
|
||||
self.header_name_map = { 'gpio_id': ['gpio id', 'id'],
|
||||
'net_name': ['net name', 'net names'],
|
||||
'bump_name': ['ball name', 'pad name'],
|
||||
'configurable': ['gpio configurable'],
|
||||
'drive_strength': ['drive strength', 'drive strength (ma)'],
|
||||
'drive_strength_ap': ['drive strength form factor', 'drive strength ap'],
|
||||
'drive_strength_dev': ['drive strength dev'],
|
||||
'pupd': ['pu/pd', 'pullup/pulldown', 'pull-up/pull-down'],
|
||||
'function': ['config', 'dir', 'SecureROM config'],
|
||||
'rom': ['rom function']}
|
||||
|
||||
if callable(getattr(self.pinconfig_class.pin_class, 'set_slew_rate', None)):
|
||||
self.header_name_map['slew_rate'] = ['slew rate', 'gpio_sr']
|
||||
self.header_name_map['slew_rate_ap'] = ['slew rate form factor', 'gpio_sr ap']
|
||||
self.header_name_map['slew_rate_dev'] = ['slew rate dev', 'gpio_sr dev']
|
||||
|
||||
if callable(getattr(self.pinconfig_class.pin_class, 'set_bus_hold', None)):
|
||||
self.header_name_map['bus_hold'] = ['bus hold', 'gpio_bh']
|
||||
|
||||
if callable(getattr(self.pinconfig_class.pin_class, 'set_input_mode', None)):
|
||||
self.header_name_map['input_mode'] = ['input mode', 'input select', 'gpio_is']
|
||||
|
||||
def create_pinconfig(self):
|
||||
return self.pinconfig_class()
|
||||
|
||||
def parse_version(self, row):
|
||||
for col in row:
|
||||
lowercase_col = col.lower()
|
||||
if 'rev' in lowercase_col or 'version' in lowercase_col or 'ver' in lowercase_col:
|
||||
return lowercase_col
|
||||
return None
|
||||
|
||||
def canonical_header(self, header):
|
||||
for canonical, options in self.header_name_map.iteritems():
|
||||
if header.lower() in options:
|
||||
return canonical
|
||||
return None
|
||||
|
||||
def find_headers(self, row, rom):
|
||||
lowercase_row = map(lambda x: x.lower(), row)
|
||||
columns_per_target = {}
|
||||
columns_per_config = collections.defaultdict(dict)
|
||||
per_config_columns = set()
|
||||
ignore_missing_keys = set()
|
||||
missing_keys = set()
|
||||
|
||||
# create a mapping from canonical header names to column indices
|
||||
header_map = {}
|
||||
for idx in range(len(lowercase_row)):
|
||||
header = lowercase_row[idx]
|
||||
canonical = self.canonical_header(header)
|
||||
if canonical is None:
|
||||
continue
|
||||
header_map[canonical] = idx
|
||||
|
||||
# some parameters can be specified per config (ap/dev). figure out
|
||||
# which if any are done that way for this spreadsheet
|
||||
config_names = ['ap', 'dev']
|
||||
for key in ['drive_strength', 'slew_rate', 'net_name']:
|
||||
config_to_key_map = {x: '{}_{}'.format(key, x) for x in config_names}
|
||||
per_config_keys = set(config_to_key_map.values())
|
||||
|
||||
if per_config_keys <= set(header_map):
|
||||
assert True, 'error message' # XXX
|
||||
# used in the loop below to skip over columns that are per config
|
||||
per_config_columns.update(per_config_keys)
|
||||
# passed back to the caller for use in parsing rows
|
||||
for config in config_names:
|
||||
columns_per_config[config][key] = header_map[config_to_key_map[config]]
|
||||
ignore_missing_keys.add(key)
|
||||
elif not per_config_keys.isdisjoint(set(header_map)):
|
||||
# either all or none of per-config columns should be present
|
||||
found = ', '.join(sorted(set(header_map) & per_config_keys))
|
||||
raise ParseError('All or none of the per-config {} columns must be specified (found {})'.format(key, found))
|
||||
else:
|
||||
# this key isn't per config, so fall through to the logic below, but
|
||||
# let the missing column checker know the per-config columns won't be present
|
||||
ignore_missing_keys.update(per_config_keys)
|
||||
|
||||
# If no per-config columns were set, then there's only one configuration.
|
||||
# Set up the key map accordingly
|
||||
if len(columns_per_config) == 0:
|
||||
for key in ['drive_strength', 'slew_rate']:
|
||||
columns_per_config['all'][key] = header_map[key]
|
||||
|
||||
for target in self.targets:
|
||||
target_header_name_map = self.per_target_headers[target]
|
||||
columns_per_target[target] = {}
|
||||
for key in self.header_name_map.keys():
|
||||
if key in per_config_columns:
|
||||
continue
|
||||
if key in target_header_name_map:
|
||||
header = target_header_name_map[key]
|
||||
# This value has a column per target, so find the per-target header in the row
|
||||
try:
|
||||
idx = lowercase_row.index(header.lower())
|
||||
except ValueError:
|
||||
sys.stderr.write('Could not find target {}\'s "{}" column "{}"\n'.format(target, key, header))
|
||||
missing_keys.add(key)
|
||||
columns_per_target[target][key] = idx
|
||||
else:
|
||||
# This value has one column for all targets, so find one of the standard headers in the row
|
||||
for header in self.header_name_map[key]:
|
||||
if header.lower() in lowercase_row:
|
||||
idx = lowercase_row.index(header.lower())
|
||||
columns_per_target[target][key] = idx
|
||||
break
|
||||
else:
|
||||
missing_keys.add(key)
|
||||
|
||||
if rom:
|
||||
for key, value in target_header_name_map.iteritems():
|
||||
if key == 'function' and value.lower() != 'SecureROM Config'.lower():
|
||||
sys.stderr.write('ROM pins should have SecureROM Config column defined\n')
|
||||
missing_keys.add(key)
|
||||
|
||||
# Some columns are optional
|
||||
missing_keys -= set(['configurable', 'input_mode'])
|
||||
# ROM Function is optional if not --rom
|
||||
if not rom:
|
||||
missing_keys -= set(['rom'])
|
||||
# Marked as being unneeded by the per-config column logic
|
||||
missing_keys -= ignore_missing_keys
|
||||
|
||||
if len(missing_keys) == 0:
|
||||
missing_keys = None
|
||||
|
||||
return columns_per_target, columns_per_config, missing_keys
|
||||
|
||||
def parse(self, filename, force=False, rom=False, sheet=None):
|
||||
if filename.endswith('.xlsx'):
|
||||
if openpyxl is None:
|
||||
sys.stderr.write('To import Excel sheets, install the openpyxl package\n')
|
||||
return None, None
|
||||
if sheet is None:
|
||||
sys.stderr.write('To import Excel sheets, specify the --sheet option\n')
|
||||
return None, None
|
||||
reader = excel_reader(filename, sheet)
|
||||
if reader is None:
|
||||
return None, None
|
||||
else:
|
||||
reader = csv.reader(open(filename, "rU"))
|
||||
cnt=0
|
||||
row_number = 0
|
||||
|
||||
# top row has version number
|
||||
row0 = reader.next()
|
||||
row_number += 1
|
||||
# next non-blank row has headers
|
||||
while True:
|
||||
row1 = reader.next()
|
||||
row_number += 1
|
||||
if len(filter(lambda x: len(x.strip()) > 0, row1)) > 0:
|
||||
break
|
||||
|
||||
version = self.parse_version(row0)
|
||||
if version is None:
|
||||
sys.stderr.write('Could not find version in 1st row\n')
|
||||
return None, None
|
||||
|
||||
try:
|
||||
columns_per_target, columns_per_config, missing = self.find_headers(row1, rom)
|
||||
except ParseError as e:
|
||||
sys.stderr.write('{}\n'.format(e))
|
||||
return None, None
|
||||
|
||||
if not missing is None:
|
||||
missing_options = {"{} ({})".format(col, " or ".join(self.header_name_map[col])) for col in missing}
|
||||
sys.stderr.write('Missing columns:\n{}\n'.format('\n '.join(missing_options)))
|
||||
return None, None
|
||||
|
||||
if len(columns_per_config) == 1:
|
||||
configs = ['all']
|
||||
else:
|
||||
configs = ['ap', 'dev']
|
||||
|
||||
pinconfigs = {}
|
||||
for target in self.targets:
|
||||
pinconfigs[target] = {}
|
||||
for config in configs:
|
||||
pinconfigs[target][config] = self.create_pinconfig()
|
||||
|
||||
have_error = False
|
||||
|
||||
for row in reader:
|
||||
row_number += 1
|
||||
if row_is_empty(row):
|
||||
continue
|
||||
if row_is_footer(row):
|
||||
break
|
||||
|
||||
for target, target_columns in columns_per_target.iteritems():
|
||||
for config, config_columns in columns_per_config.iteritems():
|
||||
columns = {}
|
||||
columns.update(config_columns)
|
||||
columns.update(target_columns)
|
||||
|
||||
try:
|
||||
parsed_props = {prop: row[column].strip() for prop, column in columns.iteritems()}
|
||||
|
||||
# If the optional 'GPIO Configurable' column is present, make sure it says y or yes
|
||||
try:
|
||||
if not parsed_props['configurable'].lower() in ('y', 'yes'):
|
||||
continue
|
||||
# And then get rid of the column, because the code below can't do anything with it
|
||||
del parsed_props['configurable']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
gpio_id = int(parsed_props['gpio_id'])
|
||||
except ValueError:
|
||||
if rom and parsed_props['gpio_id'] == '':
|
||||
continue
|
||||
else:
|
||||
raise ParseError('Invalid GPIO ID "{}"'.format(parsed_props['gpio_id']))
|
||||
|
||||
pinconfig = pinconfigs[target][config]
|
||||
try:
|
||||
pin = pinconfig.get_pin(gpio_id)
|
||||
except IndexError:
|
||||
raise ParseError('GPIO ID {} is out of range'.format(gpio_id))
|
||||
|
||||
if isinstance(pin, ReservedPin):
|
||||
raise ParseError('GPIO ID {} is reserved pin'.format(gpio_id))
|
||||
elif not pin.row_number is None:
|
||||
raise ParseError('GPIO ID {} redefined (previously defined on row {})'.format(pin.gpio_id, pin.row_number))
|
||||
pin.row_number = row_number
|
||||
|
||||
# The setters for all of the columns are predictable based on their name
|
||||
for prop, value in parsed_props.iteritems():
|
||||
if rom:
|
||||
if prop == 'function':
|
||||
if parsed_props['rom'].lower() in ('y', 'yes'):
|
||||
value = parsed_props['function']
|
||||
else:
|
||||
value = 'disabled'
|
||||
if prop == 'net_name':
|
||||
if not parsed_props['rom'].lower() in ('y', 'yes'):
|
||||
value = ''
|
||||
if prop != 'gpio_id' and prop != 'rom':
|
||||
setter = getattr(pin, 'set_' + prop)
|
||||
try:
|
||||
setter(value)
|
||||
except KeyError:
|
||||
raise ParseError('Invalid value "{}" for key {} in column "{}"'.format(value, prop, row1[columns[prop]]))
|
||||
except ParseError as e:
|
||||
sys.stderr.write('Row {:3}, Config {:3}: {}\n'.format(row_number, config, e))
|
||||
have_error = True
|
||||
|
||||
if not have_error or force:
|
||||
return version, pinconfigs
|
||||
else:
|
||||
return None, None
|
||||
|
||||
def tab_align(data_matrix, tabwidth=8, min_spacing_slack=1, min_column_tabs={}):
|
||||
def rounduptab(x):
|
||||
return int(((x + float(tabwidth-1))/tabwidth))*tabwidth
|
||||
def tab(x):
|
||||
if x % tabwidth:
|
||||
return rounduptab(x)
|
||||
else:
|
||||
return x+tabwidth
|
||||
num_cols = max(map(len, data_matrix))
|
||||
|
||||
# Compute max number of characters seen for each column
|
||||
max_char_counts = [0]*num_cols
|
||||
for column, min_width in min_column_tabs.iteritems():
|
||||
max_char_counts[column] = min_width*tabwidth
|
||||
def grow_count(new_row):
|
||||
for i in range(len(new_row)):
|
||||
max_char_counts[i] = max(max_char_counts[i], rounduptab(len(new_row[i]) + min_spacing_slack))
|
||||
for row in data_matrix:
|
||||
grow_count(row)
|
||||
|
||||
def pad_tabs(item, required_length):
|
||||
length = len(item)
|
||||
tabcount = 0
|
||||
while length < required_length:
|
||||
tabcount+=1
|
||||
length=tab(length)
|
||||
return "%s%s" % (item, '\t'*tabcount)
|
||||
def format_row(row):
|
||||
return "".join([pad_tabs(a, b) for (a,b) in zip(row, max_char_counts)]).rstrip()
|
||||
return [ format_row(x) for x in data_matrix ]
|
||||
|
||||
def print_pinconfigs(target_config_pinconfigs, pinconfig_class, version, radar, copyright, commandline, prefix, rom, header):
|
||||
array_type = pinconfig_class.array_type
|
||||
num_domains = len(pinconfig_class.domain_info)
|
||||
if hasattr(pinconfig_class, 'output_domains'):
|
||||
if pinconfig_class.output_domains < num_domains:
|
||||
num_domains = pinconfig_class.output_domains
|
||||
|
||||
targets = sorted(target_config_pinconfigs.keys())
|
||||
configs = sorted(target_config_pinconfigs[targets[0]].keys())
|
||||
|
||||
if header:
|
||||
pinconfig_prefix = 'gpio_default_cfg'
|
||||
else:
|
||||
pinconfig_prefix = 'pinconfig'
|
||||
|
||||
if not prefix is None:
|
||||
pinconfig_prefix = "{}_{}".format(pinconfig_prefix, prefix)
|
||||
|
||||
if rom:
|
||||
assert configs == ['all']
|
||||
assert len(targets) == 1
|
||||
assert len(configs) == 1
|
||||
else:
|
||||
assert configs == ['ap', 'dev'] or configs == ['all']
|
||||
|
||||
print "/*"
|
||||
print " * Copyright (C) {} Apple Inc. All rights reserved.".format(copyright)
|
||||
print " *"
|
||||
print " * This document is the property of Apple Inc."
|
||||
print " * It is considered confidential and proprietary."
|
||||
print " *"
|
||||
print " * This document may not be reproduced or transmitted in any form,"
|
||||
print " * in whole or in part, without the express written permission of"
|
||||
print " * Apple Inc."
|
||||
print " */"
|
||||
print ""
|
||||
print "/* THIS FILE IS AUTOMATICALLY GENERATED BY tools/csvtopinconfig.py. DO NOT EDIT!"
|
||||
print " I/O Spreadsheet version: {}".format(version)
|
||||
print " I/O Spreadsheet tracker: {}".format(radar)
|
||||
print
|
||||
print " Generated with oldskool_csvtopinconfig.py. For new projects, use the regular csvtopinconfig.py"
|
||||
print " Conversion command: {}".format(commandline)
|
||||
print "*/"
|
||||
print ""
|
||||
|
||||
if header:
|
||||
print "#ifndef __PLATFORM_PINCONFIG_H"
|
||||
print "#define __PLATFORM_PINCONFIG_H"
|
||||
print ""
|
||||
else:
|
||||
print "#include <debug.h>"
|
||||
print "#include <drivers/apple/gpio.h>"
|
||||
print "#include <platform.h>"
|
||||
print "#include <platform/soc/hwregbase.h>"
|
||||
print "#include <stdint.h>"
|
||||
if len(targets) > 1:
|
||||
print "#include <target/boardid.h>"
|
||||
print ""
|
||||
|
||||
for target in targets:
|
||||
for config in configs:
|
||||
if len(targets) == 1:
|
||||
if len(configs) == 1:
|
||||
name = pinconfig_prefix
|
||||
else:
|
||||
name = '{}_{}'.format(pinconfig_prefix, config.lower())
|
||||
else:
|
||||
if len(configs) == 1:
|
||||
name = '{}_{}'.format(pinconfig_prefix, target.lower())
|
||||
else:
|
||||
name = '{}_{}{}'.format(pinconfig_prefix, target.lower(), config.lower())
|
||||
print target_config_pinconfigs[target][config].to_pinconfig_struct(name, array_type, num_domains)
|
||||
|
||||
if header:
|
||||
print ""
|
||||
print "#endif /* __PLATFORM_PINCONFIG_H */"
|
||||
else:
|
||||
print 'struct pinconfig_map {'
|
||||
print '\tuint32_t board_id;'
|
||||
print '\tuint32_t board_id_mask;'
|
||||
print '\tconst {} *pinconfigs[GPIOC_COUNT];'.format(array_type)
|
||||
print '};'
|
||||
print ''
|
||||
print 'static const struct pinconfig_map cfg_map[] = {'
|
||||
if len(targets) == 1:
|
||||
if len(configs) == 1:
|
||||
# just one target and one config, so make a map that spits out the same pinconfig
|
||||
# for all board IDs
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_{}'.format(pinconfig_prefix, x), range(num_domains)))
|
||||
print '\t{{ 0, 0, {{ {} }} }},'.format(pinconfigs)
|
||||
elif len(configs) == 2:
|
||||
# One target, but ap/dev configs, so make a map that selects ap/dev based on the lsb
|
||||
# of the board ID
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_ap_{}'.format(pinconfig_prefix, x), range(num_domains)))
|
||||
print '\t{{ 0, 1, {{ {} }} }},'.format(pinconfigs)
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_dev_{}'.format(pinconfig_prefix, x), range(num_domains)))
|
||||
print '\t{{ 1, 1, {{ {} }} }},'.format(pinconfigs)
|
||||
else:
|
||||
assert False, 'Can\'t handle more than 2 configs per target'
|
||||
else:
|
||||
for target in targets:
|
||||
if len(configs) == 1:
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_{}_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains)))
|
||||
print '\t{{ TARGET_BOARD_ID_{}AP, ~1, {{ {} }} }},'.format(target.upper(), pinconfigs)
|
||||
elif len(configs) == 2:
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_{}ap_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains)))
|
||||
print '\t{{ TARGET_BOARD_ID_{}AP, ~0, {{ {} }} }},'.format(target.upper(), pinconfigs)
|
||||
pinconfigs = ', '.join(map(lambda x: '{}_{}dev_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains)))
|
||||
print '\t{{ TARGET_BOARD_ID_{}DEV, ~0, {{ {} }} }},'.format(target.upper(), pinconfigs)
|
||||
else:
|
||||
assert False, 'Can\'t handle more than 2 configs per target'
|
||||
print '};'
|
||||
print ''
|
||||
if prefix is None:
|
||||
print 'const {} * target_get_default_gpio_cfg(uint32_t gpioc)'.format(array_type)
|
||||
else:
|
||||
print 'const {} * target_get_{}_gpio_cfg(int gpioc)'.format(array_type, prefix)
|
||||
print '{'
|
||||
print '\tstatic const struct pinconfig_map *selected_map = NULL;'
|
||||
print ''
|
||||
print '\tif (selected_map == NULL) {';
|
||||
print '\t\tuint32_t board_id = platform_get_board_id();'
|
||||
print '\t\tfor (unsigned i = 0; i < sizeof(cfg_map)/sizeof(cfg_map[0]); i++) {'
|
||||
print '\t\t\tif ((board_id & cfg_map[i].board_id_mask) == cfg_map[i].board_id) {'
|
||||
print '\t\t\t\tselected_map = &cfg_map[i];'
|
||||
print '\t\t\t\tbreak;'
|
||||
print '\t\t\t}'
|
||||
print '\t\t}'
|
||||
print ''
|
||||
print '\t\tif (selected_map == NULL)'
|
||||
print '\t\t\tpanic("no default pinconfig for board id %u", board_id);'
|
||||
print '\t}'
|
||||
print ''
|
||||
print '\tASSERT(gpioc < GPIOC_COUNT);'
|
||||
print '\treturn selected_map->pinconfigs[gpioc];'
|
||||
print '}'
|
||||
|
||||
|
||||
def main():
|
||||
soc_classes = collections.OrderedDict()
|
||||
soc_classes['h4a'] = H4APinConfig
|
||||
soc_classes['h5p'] = H5PPinConfig
|
||||
soc_classes['h5g'] = H5GPinConfig
|
||||
soc_classes['alcatraz'] = AlcatrazPinConfig
|
||||
soc_classes['h6'] = AlcatrazPinConfig
|
||||
soc_classes['h6p'] = AlcatrazPinConfig
|
||||
soc_classes['fiji'] = FijiPinConfig
|
||||
soc_classes['h7p'] = FijiPinConfig
|
||||
soc_classes['capri'] = CapriPinConfig
|
||||
soc_classes['h7g'] = CapriPinConfig
|
||||
soc_classes['m7'] = M7PinConfig
|
||||
soc_classes['h8p'] = MauiPinConfig
|
||||
soc_classes['maui'] = MauiPinConfig
|
||||
soc_classes['h8g'] = ElbaPinConfig
|
||||
soc_classes['elba'] = ElbaPinConfig
|
||||
soc_classes['h9p'] = CaymanPinConfig
|
||||
soc_classes['cayman'] = CaymanPinConfig
|
||||
soc_classes['m8'] = M8PinConfig
|
||||
|
||||
argparser = argparse.ArgumentParser(description='Converts CSV file to iBoot pinconfig.h format')
|
||||
argparser.add_argument('--soc', required=True, choices=soc_classes.keys())
|
||||
argparser.add_argument('--config-column', type=str, action='append')
|
||||
argparser.add_argument('--pupd-column', type=str, action='append')
|
||||
argparser.add_argument('--prefix', type=str, help='prefix for auto-generated variable and function names')
|
||||
argparser.add_argument('--copyright', type=str, default='YEAR', help='Year(s) to put in copyright notice')
|
||||
argparser.add_argument('--netname-column', type=str, action='append')
|
||||
argparser.add_argument('--radar', type=str, help='Link to spreadsheet tracker radar')
|
||||
argparser.add_argument('--force', action="store_true", help='attempt ouput even when there are parsing errors')
|
||||
argparser.add_argument('--rom', action="store_true", help='all pins except ROM function pins are disabled')
|
||||
argparser.add_argument('--header', action="store_true", help='generate a C header file instead of a C source file')
|
||||
argparser.add_argument('--sheet', default=None, help='sheet to read from when reading Excel files')
|
||||
argparser.add_argument('filename')
|
||||
|
||||
args = argparser.parse_args()
|
||||
|
||||
pinconfig_class = soc_classes[args.soc]
|
||||
|
||||
per_target_headers = collections.defaultdict(dict)
|
||||
|
||||
if not args.config_column is None:
|
||||
for item in map(lambda x: x.split(":"), args.config_column):
|
||||
if len(item) == 1:
|
||||
target = None
|
||||
column = item[0]
|
||||
elif len(item) == 2:
|
||||
target = item[0].upper()
|
||||
column = item[1]
|
||||
else:
|
||||
sys.stderr.write("Invalid config-column parameter\n")
|
||||
sys.exit(1)
|
||||
if 'function' in per_target_headers[target]:
|
||||
sys.stderr.write("Duplicate target in --config-column: \"{}\"".format(target))
|
||||
sys.exit(1)
|
||||
per_target_headers[target]['function'] = column
|
||||
|
||||
if not args.pupd_column is None:
|
||||
for item in map(lambda x: x.split(":"), args.pupd_column):
|
||||
if len(item) == 1:
|
||||
target = None
|
||||
column = item[0]
|
||||
elif len(item) == 2:
|
||||
target = item[0].upper()
|
||||
column = item[1]
|
||||
else:
|
||||
sys.stderr.write("Invalid pupd-column parameter\n")
|
||||
sys.exit(1)
|
||||
if 'pupd' in per_target_headers[target]:
|
||||
sys.stderr.write("Duplicate target in --pupd-column: \"{}\"".format(target))
|
||||
sys.exit(1)
|
||||
per_target_headers[target]['pupd'] = column
|
||||
|
||||
if not args.netname_column is None:
|
||||
for item in map(lambda x: x.split(":"), args.netname_column):
|
||||
if len(item) == 1:
|
||||
target = None
|
||||
column = item[0]
|
||||
elif len(item) == 2:
|
||||
target = item[0].upper()
|
||||
column = item[1]
|
||||
else:
|
||||
sys.stderr.write("Invalid netname-column parameter\n")
|
||||
sys.exit(1)
|
||||
if 'net_name' in per_target_headers[target]:
|
||||
sys.stderr.write("Duplicate target in --netname-column: \"{}\"".format(target))
|
||||
sys.exit(1)
|
||||
per_target_headers[target]['net_name'] = column
|
||||
|
||||
if not per_target_headers:
|
||||
per_target_headers[None] = {}
|
||||
|
||||
parser = CSVParser(pinconfig_class, per_target_headers)
|
||||
version, pinconfigs = parser.parse(args.filename, force=args.force, rom=args.rom, sheet=args.sheet)
|
||||
if pinconfigs is None:
|
||||
sys.exit(1)
|
||||
|
||||
def pretty_arg(arg):
|
||||
if arg == args.filename:
|
||||
return '<filename>'
|
||||
else:
|
||||
return pipes.quote(arg)
|
||||
commandline = './tools/oldskool_csvtopinconfig.py {}'.format(' '.join(pretty_arg(a) for a in sys.argv[1:]))
|
||||
|
||||
print_pinconfigs(pinconfigs, pinconfig_class=pinconfig_class, version=version, radar=args.radar, copyright=args.copyright, commandline=commandline, prefix=args.prefix, rom=args.rom, header=args.header)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/python
|
||||
import re
|
||||
import os
|
||||
import hashlib
|
||||
import hmac
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
argparser = argparse.ArgumentParser(description='Parses obfuscated logging from iBoot release builds')
|
||||
argparser.add_argument('--src', required=True, help='iBoot source tree corresponding to log')
|
||||
argparser.add_argument('-v', '--verbose', action='store_true', default=False, help='Attempts to load log string from source file instead of simplying printing the source file and line number')
|
||||
argparser.add_argument('filename', nargs='?', default=None, help='File to read log from (omit for stdin)')
|
||||
|
||||
args = argparser.parse_args()
|
||||
|
||||
id_file_map = {}
|
||||
|
||||
directory = args.src
|
||||
|
||||
base_hmac = hmac.new('obfuscation of filename for release build logging', '', hashlib.sha1)
|
||||
|
||||
for (path, dirnames, filenames) in os.walk(directory):
|
||||
# strip the part of the pathname we supplied off the pathname
|
||||
shortened_path = path[len(directory)+1:]
|
||||
# don't iterate into paths we don't care about
|
||||
try:
|
||||
dirnames.remove('.svn')
|
||||
except ValueError:
|
||||
pass
|
||||
# only look at .c and .cpp files in the build directory, be more permissive elsewhere
|
||||
if shortened_path.startswith('build/'):
|
||||
filenames = [x for x in filenames if x.endswith('.c') or x.endswith('.cpp')]
|
||||
for filename in filenames:
|
||||
full_path = os.path.join(shortened_path, filename)
|
||||
|
||||
current_hmac = base_hmac.copy()
|
||||
current_hmac.update(full_path)
|
||||
|
||||
file_id = current_hmac.hexdigest()[:16]
|
||||
|
||||
if file_id in id_file_map and id_file_map[file_id] != full_path:
|
||||
print 'Warning: Files "{}" and "{}" both have the same ID {}'.format(full_path, id_file_map[file_id], file_id)
|
||||
else:
|
||||
id_file_map[file_id] = full_path
|
||||
|
||||
file_cache = {}
|
||||
def get_src_line(filename, line_number):
|
||||
|
||||
if filename not in file_cache:
|
||||
file_path = os.path.join(directory, filename)
|
||||
with open(file_path) as f:
|
||||
lines = [x.strip() for x in f]
|
||||
file_cache[filename] = lines
|
||||
|
||||
return file_cache[filename][line_number - 1]
|
||||
|
||||
def parse_src_line(src_line):
|
||||
result = re.match(r'dprintf\s*\(\s*[a-zA-Z0-9_]+,\s*"([^"]*)".*', src_line)
|
||||
|
||||
if result is not None:
|
||||
return result.group(1)
|
||||
else:
|
||||
return src_line
|
||||
|
||||
def parse_log(f):
|
||||
log_re = re.compile(r'^(.*\|)?([0-9a-f]{16}):(\d+)$')
|
||||
for line in f:
|
||||
line = line.rstrip()
|
||||
result = log_re.match(line)
|
||||
if result is not None:
|
||||
timestamp = result.group(1) or ''
|
||||
file_id = result.group(2)
|
||||
line_number = int(result.group(3))
|
||||
if file_id in id_file_map:
|
||||
filename = id_file_map[file_id]
|
||||
if args.verbose:
|
||||
src_line = get_src_line(filename, line_number)
|
||||
print '{}{} [{}:{}]'.format(timestamp, parse_src_line(src_line), filename, line_number)
|
||||
else:
|
||||
print '{}{}:{}'.format(timestamp, filename, line_number)
|
||||
continue
|
||||
print line
|
||||
|
||||
if args.filename is not None:
|
||||
with open(args.filename) as f:
|
||||
parse_log(f)
|
||||
else:
|
||||
parse_log(sys.stdin)
|
||||
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/tclsh
|
||||
#
|
||||
# Takes a PC trace from simulation and annotates it with function/line number information
|
||||
#
|
||||
# The PC trace is assumed to have one PC per line; the first likely-looking number on each line is assumed to be the PC.
|
||||
#
|
||||
# usage: pctrace-decoder <PC trace file> <path to symbolicated binary in build directory>
|
||||
#
|
||||
|
||||
package require log
|
||||
package require fileutil
|
||||
|
||||
################################################################################
|
||||
# utilities
|
||||
|
||||
proc fatal {msg} {
|
||||
global errorInfo
|
||||
|
||||
::log::log error $msg
|
||||
# if {$errorInfo != ""} {
|
||||
# ::log::log debug $errorInfo
|
||||
# }
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
proc generateBatch {pctrace batchfile} {
|
||||
|
||||
::log::log debug "parsing trace $pctrace"
|
||||
|
||||
::fileutil::foreachLine inputLine $pctrace {
|
||||
foreach tok $inputLine {
|
||||
switch -glob $tok {
|
||||
[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] {
|
||||
set addresses($tok) ""
|
||||
break
|
||||
}
|
||||
|
||||
0x[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] {
|
||||
# hex number
|
||||
set addresses([string range $tok 2 end]) ""
|
||||
break
|
||||
}
|
||||
|
||||
default {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if {![array exists addresses]} {
|
||||
fatal "no recognisable data in '$pctrace' (maybe need a smarter parser?)"
|
||||
}
|
||||
|
||||
set batch [open $batchfile w]
|
||||
|
||||
::log::log debug "generating batchfile $batchfile"
|
||||
foreach address [array names addresses] {
|
||||
puts $batch [format "echo &%s&" $address]
|
||||
puts $batch [format "info line *0x%s" $address]
|
||||
}
|
||||
|
||||
close $batch
|
||||
}
|
||||
|
||||
################################################################################
|
||||
proc runBatch {symfile batchfile batchresult} {
|
||||
|
||||
::log::log debug "generating symbol data $batchresult"
|
||||
|
||||
if {[catch {exec gdb -n -batch -x $batchfile $symfile > $batchresult} result]} {
|
||||
fatal "gdb failed: $result"
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
proc annotateTrace {pctrace batchresult traceoutput condensed} {
|
||||
|
||||
::log::log debug "reading batch results"
|
||||
|
||||
::fileutil::foreachLine inputLine $batchresult {
|
||||
|
||||
if {[scan $inputLine "&%\[^&\]&Line %d of \"%\[^\"\]\" starts at address %x <%\[^>\]> and ends at %x <%\[^>\]>" key lineNumber fileName startAddr startSym endAddr endSym] == 7} {
|
||||
if {[scan $startSym "%\[^+\]+%x" sym addr] == 2} {
|
||||
set startSym $sym
|
||||
}
|
||||
if {[scan $endSym "%\[^+\]+%x" sym addr] == 2} {
|
||||
set endSym $sym
|
||||
}
|
||||
|
||||
if {$startSym == $endSym} {
|
||||
set addrinfo($key) [format "%s:%s:%d" $fileName $startSym $lineNumber]
|
||||
} else {
|
||||
set addrinfo($key) [format "%s:%s/%s:%d" $fileName $startSym $endSym $lineNumber]
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if {[scan $inputLine "&%\[^&\]&No line number information available for address %x <%\[^>\]>" key address addrSym] == 3} {
|
||||
if {[scan $addrSym "%\[^+\]+%x" sym addr] == 2} {
|
||||
set addrSym $sym
|
||||
}
|
||||
set addrinfo($key) [format "%s" $addrSym]
|
||||
continue
|
||||
}
|
||||
|
||||
log::log warning "unexpected batch result '$inputLine'"
|
||||
}
|
||||
|
||||
set out [open $traceoutput w]
|
||||
set cout [open $condensed w]
|
||||
::log::log debug "annotating trace"
|
||||
|
||||
set lastSuffix ""
|
||||
::fileutil::foreachLine inputLine $pctrace {
|
||||
set address ""
|
||||
foreach tok $inputLine {
|
||||
switch -glob $tok {
|
||||
[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] {
|
||||
set address $tok
|
||||
break
|
||||
}
|
||||
|
||||
0x[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] {
|
||||
# hex number
|
||||
set address [string range $tok 2 end]
|
||||
break
|
||||
}
|
||||
|
||||
default {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if {($address != "") && ([array names addrinfo -exact $address] != "")} {
|
||||
set suffix $addrinfo($address)
|
||||
if {$suffix != $lastSuffix} {
|
||||
puts $cout $suffix
|
||||
set lastSuffix $suffix
|
||||
}
|
||||
} else {
|
||||
set suffix ""
|
||||
}
|
||||
puts $out [format "%s -- %s" $inputLine $suffix]
|
||||
}
|
||||
close $out
|
||||
close $cout
|
||||
}
|
||||
|
||||
################################################################################
|
||||
|
||||
proc main {} {
|
||||
|
||||
global argv
|
||||
|
||||
# logging
|
||||
::log::lvSuppress error 0
|
||||
::log::lvSuppress warning 0
|
||||
::log::lvSuppress notice 0
|
||||
::log::lvSuppress debug 1
|
||||
|
||||
set pctrace [lindex $argv 0]
|
||||
set symfile [lindex $argv 1]
|
||||
|
||||
if {![file readable $pctrace]} {
|
||||
fatal "missing or unreadable PC trace file '$pctrace'"
|
||||
}
|
||||
if {![file readable $symfile]} {
|
||||
fatal "missing or unreadable symbol file '$symfile'"
|
||||
}
|
||||
|
||||
# generate a gdb batch file
|
||||
::log::log notice "generating gdb batch from PC trace..."
|
||||
set batchfile [::fileutil::tempfile ptrace_decoder_batch_]
|
||||
generateBatch $pctrace $batchfile
|
||||
|
||||
# invoke gdb against the batch file
|
||||
::log::log notice "processing batch..."
|
||||
set batchresult [::fileutil::tempfile ptrace_decoder_result_]
|
||||
runBatch $symfile $batchfile $batchresult
|
||||
file delete $batchfile
|
||||
|
||||
# annotate the pctrace with results from the result file
|
||||
::log::log notice "annotating trace..."
|
||||
set traceoutput [format "%s.annotated" $pctrace]
|
||||
set condensed [format "%s.condensed" $pctrace]
|
||||
annotateTrace $pctrace $batchresult $traceoutput $condensed
|
||||
file delete $batchresult
|
||||
::log::log notice "Annotated output in '$traceoutput', condensed output in '$condensed'"
|
||||
}
|
||||
main
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Find .d files that are produced by compilation and produce a list of all the files they represent
|
||||
#
|
||||
|
||||
# Find source and headers that were built
|
||||
rm -f built.list built.tmp
|
||||
for dfile in `find build -name "*.d"`; do
|
||||
echo "parsing $dfile"
|
||||
cat $dfile | tr : \\n | tail +2 | tr -d \\ | tr ' ' \\n | grep \^\[a-z\] >> built.tmp
|
||||
done
|
||||
cat built.tmp | sort | uniq > built.list
|
||||
|
||||
# Find all sources
|
||||
echo "finding base sources"
|
||||
rm -f all.list
|
||||
find . -name "*.[chS]" | cut -c 3-1000 > all.list
|
||||
|
||||
# Make a list of what's not used
|
||||
rm -f delete.list
|
||||
for file in `cat all.list`; do
|
||||
/bin/echo -n "checking for $file ... "
|
||||
if grep -q $file built.list; then
|
||||
echo "found, keeping"
|
||||
else
|
||||
# handle exceptions
|
||||
case $file in
|
||||
platform/defaults/template.c ) echo "protected" ;;
|
||||
platform/generic/template.c ) echo "protected" ;;
|
||||
tools/bhc.c ) echo "protected" ;;
|
||||
|
||||
* )
|
||||
echo "not found, deleting"
|
||||
echo $file >> delete.list
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
# Delete unwanted files
|
||||
for file in `cat delete.list`; do
|
||||
rm $file
|
||||
done
|
||||
|
||||
rm -f built.list built.tmp all.list delete.list
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require 'openssl'
|
||||
|
||||
if ARGV.length < 2
|
||||
print File.basename(__FILE__) + " <outfile> ppn-fw=<fw file> [ ppn-fw-args=<fwa file> ]\n"
|
||||
exit 1
|
||||
end
|
||||
|
||||
outFn = ARGV[0]
|
||||
inPairs = ARGV[1..-1]
|
||||
|
||||
blob = OpenSSL::ASN1::Sequence([], 1, :IMPLICIT, :APPLICATION)
|
||||
inPairs.each do |pair|
|
||||
key, val = pair.split('=', 2)
|
||||
|
||||
blob.value << OpenSSL::ASN1::UTF8String(key)
|
||||
blob.value << OpenSSL::ASN1::OctetString(File.read(val))
|
||||
end
|
||||
|
||||
File.open(outFn, 'w') {|f| f.write(blob.to_der) }
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
if (@ARGV < 1) {
|
||||
print "USAGE: rom_release <platform>\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$soc = shift @ARGV;
|
||||
$soc = lc($soc);
|
||||
|
||||
print "Building SecureROM\n";
|
||||
system("sudo ~rc/bin/buildit -arch armv7 -project rom -noverify . -- APPLICATIONS=SecureROM TARGETS=$soc > /tmp/buildit.log 2> /tmp/buildit.error");
|
||||
|
||||
print "Generating tarball\n";
|
||||
$user = getlogin();
|
||||
system("sudo chown -R $user:apple_ga /tmp/rom.roots");
|
||||
system("sudo chmod -R oga+w /tmp/rom.roots/rom");
|
||||
system("tar -C /tmp -zcvf rom.roots.tgz rom.roots 2> /tmp/rom.tgz.log");
|
||||
system("mv ./rom.roots.tgz /tmp");
|
||||
|
||||
$dropbox = "/Volumes/CoreOS/SecureROM-Drops";
|
||||
|
||||
print "Copying to DropBox\n";
|
||||
$seq = 1;
|
||||
if (-d "$dropbox/$soc") {
|
||||
system("ls -ltU ". "$dropbox/$soc" . " | awk '{ if(substr(\$NF,11,1) > max) {max=substr(\$NF,11,1);} } END {print max\}' > /tmp/awk.out");
|
||||
open(FILE, "</tmp/awk.out");
|
||||
while(<FILE>) {
|
||||
chomp($_);
|
||||
$seq = $_;
|
||||
$seq = $seq + 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
print "First release, setting up directory structure\n";
|
||||
system("mkdir $dropbox/$soc");
|
||||
}
|
||||
system("mkdir $dropbox/$soc/SecureROM-$seq");
|
||||
system("cp /tmp/rom.roots.tgz $dropbox/$soc/SecureROM-$seq");
|
||||
|
||||
print "Moving files\n";
|
||||
system("cp /tmp/rom.roots/rom~sym/build/"."$soc"."si-SecureROM-RELEASE/SecureROM.bin $dropbox/$soc/SecureROM-$seq/");
|
||||
system("cp /tmp/rom.roots/rom~sym/build/"."$soc"."si-SecureROM-RELEASE/SecureROM.sys $dropbox/$soc/SecureROM-$seq/");
|
||||
|
||||
print "Done.\n";
|
||||
@@ -0,0 +1,183 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# After pickfiles, scrub the iBoot tree of anything not relevant to the current secure ROM
|
||||
#
|
||||
|
||||
supported_platforms="t8002 t8010"
|
||||
unwanted_dirs_files_list="target docs build BuildiBoot.sh"
|
||||
|
||||
if [ $# -ne "1" ] ; then
|
||||
echo "USAGE: scrub_SecureROM <platform>"
|
||||
exit
|
||||
fi
|
||||
|
||||
found=0
|
||||
for word in $supported_platforms; do
|
||||
if [ $1 == $word ] ; then
|
||||
found=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $found != 1 ]; then
|
||||
echo "Unsupported platform $1"
|
||||
exit
|
||||
fi
|
||||
|
||||
# init variables
|
||||
securerom_platform=$1
|
||||
new_working_dir="/tmp/iBoot.$securerom_platform.scrubbed"
|
||||
|
||||
# setup new working dir
|
||||
rm -rf $new_working_dir
|
||||
cp -R $PWD $new_working_dir
|
||||
|
||||
# switch to new working dir
|
||||
pushd $new_working_dir > /dev/null
|
||||
|
||||
# other apps
|
||||
for dir in `ls -d apps/*`; do
|
||||
case $dir in
|
||||
apps/SecureROM )
|
||||
;;
|
||||
* )
|
||||
echo removing directory $dir
|
||||
rm -rf $dir
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# other platforms
|
||||
for dir in `ls -d platform/*`; do
|
||||
case $dir in
|
||||
platform/generic )
|
||||
;;
|
||||
platform/defaults )
|
||||
;;
|
||||
platform/$securerom_platform )
|
||||
;;
|
||||
* )
|
||||
echo removing directory $dir
|
||||
rm -rf $dir
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# all targets
|
||||
rm -rf targets
|
||||
|
||||
# whitelist Makefiles
|
||||
for mk in `find makefiles -type f`; do
|
||||
case $mk in
|
||||
makefiles/build.mk )
|
||||
;;
|
||||
makefiles/config.mk )
|
||||
;;
|
||||
makefiles/device_map.mk )
|
||||
;;
|
||||
makefiles/headers.mk )
|
||||
;;
|
||||
makefiles/lib.mk )
|
||||
;;
|
||||
makefiles/libraries.mk )
|
||||
;;
|
||||
makefiles/macros.mk )
|
||||
;;
|
||||
makefiles/main.mk )
|
||||
;;
|
||||
makefiles/main-nested-module.mk )
|
||||
;;
|
||||
makefiles/macros.mk )
|
||||
;;
|
||||
makefiles/tools.mk )
|
||||
;;
|
||||
makefiles/SecureROM.mk )
|
||||
;;
|
||||
* )
|
||||
echo removing file $mk
|
||||
rm -f $mk
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# whitelist tools
|
||||
for tool in `find tools -type f`; do
|
||||
case $tool in
|
||||
tools/check_liblist.py )
|
||||
;;
|
||||
tools/lldb_init_iboot.py )
|
||||
;;
|
||||
tools/lldb_os_iboot.py )
|
||||
;;
|
||||
tools/generate_debug_hashes.py )
|
||||
;;
|
||||
tools/macho_post_process.py )
|
||||
;;
|
||||
tools/macho.py )
|
||||
;;
|
||||
* )
|
||||
echo removing file $tool
|
||||
rm -f $tool
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# anything referring to other architectures
|
||||
for word in $supported_platforms; do
|
||||
if [ $word != $securerom_platform ] ; then
|
||||
find . -name "*$word*" -delete
|
||||
fi
|
||||
done
|
||||
|
||||
# version control directories
|
||||
rm -rf `find . -name ".svn" -type dir`
|
||||
|
||||
# strip bad words
|
||||
for word in $supported_platforms; do
|
||||
if [ $word != $securerom_platform ] ; then
|
||||
files=`find . -type f | xargs grep -l $word`
|
||||
for file in $files; do
|
||||
tfile=`mktemp scrub.XXXX`
|
||||
sed -e "s/$word//g" < $file > $tfile
|
||||
cat $tfile > $file
|
||||
rm $tfile
|
||||
done
|
||||
fi
|
||||
|
||||
# strip irrelevant test files
|
||||
dir="apps/SecureROM/tests/images_$word"
|
||||
if [ -d "$dir" ] ; then
|
||||
echo removing directory $dir
|
||||
rm -r "$dir"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# rules.mk with no adjacent sources
|
||||
for mk in `find . -name rules.mk`; do
|
||||
dir=`dirname $mk`
|
||||
files=`find $dir -name "*.[chS]"`
|
||||
if [ -z "$files" ]; then
|
||||
echo removing file $mk
|
||||
rm -f $mk
|
||||
fi
|
||||
done
|
||||
|
||||
# empty directories
|
||||
for dir in `find -d . -type dir`; do
|
||||
files=`ls -1 $dir`
|
||||
if [ -z "$files" ]; then
|
||||
echo removing empty directory $dir
|
||||
rmdir $dir
|
||||
fi
|
||||
done
|
||||
|
||||
# remove all unwanted directories
|
||||
for word in $unwanted_dirs_files_list; do
|
||||
echo removing directory $word
|
||||
rm -rf $word
|
||||
done
|
||||
|
||||
# back to original working dir
|
||||
popd > /dev/null
|
||||
|
||||
echo "Scrubbed iBoot -> $new_working_dir"
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/tclsh
|
||||
################################################################################
|
||||
#
|
||||
# Given a path to a mach-o binary and input that includes stack backtraces in
|
||||
# the format printed by iBoot's arch_backtrace_task, annotate the backtraces
|
||||
# with symbol names
|
||||
#
|
||||
# Lines in the backtrace have the form
|
||||
#
|
||||
# <whitespace><token><whitespace><hex number><hex number><newline>
|
||||
# or
|
||||
# <whitespace><hex number><hex number><newline>
|
||||
#
|
||||
|
||||
proc main { } {
|
||||
global argv
|
||||
|
||||
set binfile [lindex $argv 0]
|
||||
if {![file readable $binfile]} {
|
||||
puts "can't read '$binfile'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# run nm and parse the output
|
||||
do_nm $binfile
|
||||
|
||||
# read input lines
|
||||
while {[gets stdin line] != -1} {
|
||||
puts -nonewline $line
|
||||
if {([scan $line " %s %x %x%n" pad pc lr len] == 4) || ([scan $line " %x %x%n" pc lr len] == 3)} {
|
||||
if {$len == [string length $line]} {
|
||||
set detail [find_sym $pc]
|
||||
if {$detail != ""} {
|
||||
puts -nonewline " ($detail)"
|
||||
}
|
||||
}
|
||||
}
|
||||
puts ""
|
||||
}
|
||||
}
|
||||
|
||||
proc do_nm {binfile} {
|
||||
|
||||
global symbols
|
||||
global symbol_index
|
||||
|
||||
# puts "reading symbols from $binfile"
|
||||
|
||||
set lines [exec nm -n $binfile]
|
||||
|
||||
foreach {_addr type _sym} $lines {
|
||||
set sym [string range $_sym 1 end]
|
||||
set addr "0x$_addr"
|
||||
set symbols($addr) $sym
|
||||
}
|
||||
set symbol_index [lsort -integer [array names symbols]]
|
||||
|
||||
# puts "[llength [array names symbols]] symbols"
|
||||
}
|
||||
|
||||
proc find_sym {pc} {
|
||||
|
||||
global symbols
|
||||
global symbol_index
|
||||
|
||||
# puts "looking for $pc"
|
||||
|
||||
set candidate [lindex $symbol_index 0]
|
||||
|
||||
# if the pc is before the first symbol, it's not in the object file
|
||||
if {$pc < $candidate} {
|
||||
# puts "too low (less than $candidate)"
|
||||
return ""
|
||||
}
|
||||
|
||||
foreach addr [lrange $symbol_index 1 end] {
|
||||
|
||||
# puts "trying $addr"
|
||||
|
||||
# if the next symbol is above the pc, the candidate is where we are
|
||||
if {$pc < $addr} {
|
||||
break
|
||||
}
|
||||
|
||||
# advance the candidate
|
||||
set candidate $addr
|
||||
}
|
||||
|
||||
# the last symbol is a sentinel, if the PC is above it, it's not in the object file
|
||||
if {$pc > $addr} {
|
||||
# puts "too high"
|
||||
return ""
|
||||
}
|
||||
|
||||
set key [format 0x%08x $candidate]
|
||||
set offset [format 0x%x [expr $addr - $key]]
|
||||
return "$symbols($key)+$offset"
|
||||
}
|
||||
|
||||
main
|
||||
Reference in new issue
Block a user