first and last commit

This commit is contained in:
Chris Nutter committed 2023-07-08 13:03:17 -07:00
commit 1bf4efd552
2612 files changed
+636500

No files matched your search

+908
View File
@@ -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;
}
+237
View File
@@ -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);
}
+71
View File
@@ -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
+221
View File
@@ -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;
}
+56
View File
@@ -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
+84
View File
@@ -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;
}
+19
View File
@@ -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
+339
View File
@@ -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;
}
+57
View File
@@ -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
+133
View File
@@ -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
View File
@@ -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 $@ $^
+105
View File
@@ -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")
+14
View File
@@ -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
View File
@@ -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*/
+35
View 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())
+25
View File
@@ -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
+184
View File
@@ -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
+115
View File
@@ -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
+105
View File
@@ -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()
+988
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+47
View File
@@ -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
View File
@@ -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
+174
View File
@@ -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;
}
+10
View File
@@ -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)
+199
View File
@@ -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
View File
@@ -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()
+137
View File
@@ -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()
+102
View File
@@ -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
}
+57
View File
@@ -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:])
+893
View File
@@ -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()
+88
View File
@@ -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)
+193
View File
@@ -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
+45
View File
@@ -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
+21
View File
@@ -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) }
+45
View File
@@ -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";
+183
View File
@@ -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"
+100
View File
@@ -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