343 lines
8.4 KiB
C
343 lines
8.4 KiB
C
/*
|
|
* Copyright (C) 2007-2012, 2014 Apple Inc. All rights reserved.
|
|
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
|
*
|
|
* This document is the property of Apple Computer, Inc.
|
|
* It is considered confidential and proprietary.
|
|
*
|
|
* This document may not be reproduced or transmitted in any form,
|
|
* in whole or in part, without the express written permission of
|
|
* Apple Computer, Inc.
|
|
*/
|
|
/*
|
|
* Support for detecting and reading the iPod-style SysCfg data
|
|
*/
|
|
|
|
#include <debug.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <lib/syscfg.h>
|
|
#include <lib/blockdev.h>
|
|
#include <sys/menu.h>
|
|
|
|
static u_int32_t syscfgKeyCount;
|
|
static uint8_t *syscfgData;
|
|
static size_t syscfgDataLength;
|
|
|
|
struct syscfgHeader {
|
|
u_int32_t shMagic;
|
|
#define kSysCfgHeaderMagic 'SCfg'
|
|
u_int32_t shSize;
|
|
u_int32_t shMaxSize;
|
|
u_int32_t shVersion;
|
|
u_int32_t shBigEndian;
|
|
u_int32_t shKeyCount;
|
|
};
|
|
|
|
void syscfg_reinit(void)
|
|
{
|
|
free(syscfgData);
|
|
syscfgDataLength = 0;
|
|
syscfgData = NULL;
|
|
syscfgKeyCount = 0;
|
|
}
|
|
|
|
int
|
|
do_syscfg(int argc, struct cmd_arg *args)
|
|
{
|
|
struct syscfgHeader hdr;
|
|
struct syscfgMemEntry entry;
|
|
int index, curArg;
|
|
size_t size, i, j;
|
|
char *sfx;
|
|
bool isExt;
|
|
size_t lowExt;
|
|
size_t highExt;
|
|
size_t extSize = 0;
|
|
uint8_t *buffer;
|
|
bool showExt = false;
|
|
|
|
if (NULL == syscfgData) {
|
|
printf("syscfg is not initialized!\n");
|
|
return(0);
|
|
}
|
|
|
|
if (syscfgDataLength < sizeof(struct syscfgHeader))
|
|
return (0);
|
|
|
|
memcpy(&hdr, syscfgData, sizeof(struct syscfgHeader));
|
|
printf("version 0x%08x with %d entries\n\n", hdr.shVersion, hdr.shKeyCount);
|
|
|
|
lowExt = hdr.shMaxSize;
|
|
highExt = hdr.shSize;
|
|
|
|
for (index = 0; ; index++) {
|
|
if (!syscfgFindByIndex(index, &entry))
|
|
break;
|
|
|
|
if (argc > 1) {
|
|
|
|
/* When parameters are present, always show in extended mode */
|
|
showExt = true;
|
|
|
|
/* If the first parameter is ext, assume match in all cases by skipping the match block */
|
|
if(strcmp("ext", args[1].str) != 0) {
|
|
|
|
/* Scan the list of arguments looking for a match */
|
|
for (curArg = 1; curArg < argc; curArg ++) {
|
|
if (((entry.seTag >> 24) & 0xFF) == args[curArg].str[0] &&
|
|
((entry.seTag >> 16) & 0xFF) == args[curArg].str[1] &&
|
|
((entry.seTag >> 8) & 0xFF) == args[curArg].str[2] &&
|
|
((entry.seTag >> 0) & 0xFF) == args[curArg].str[3])
|
|
break;
|
|
}
|
|
if (curArg == argc) continue;
|
|
}
|
|
}
|
|
|
|
sfx = "";
|
|
|
|
/* Is this a standard entry or an Offset entry? */
|
|
if (entry.seDataOffset != 0) {
|
|
size = entry.seDataSize;
|
|
if (size > sizeof(entry.seData)) {
|
|
|
|
/* The Size of the entry is too big for the structure
|
|
* Should it be truncated? */
|
|
if (!showExt) {
|
|
size = sizeof(entry.seData);
|
|
sfx = "...";
|
|
}
|
|
}
|
|
if (entry.seDataOffset + entry.seDataSize > hdr.shMaxSize) {
|
|
printf("entry->seDataOffset not within syscfgData");
|
|
return(0);
|
|
}
|
|
|
|
/* Allocate the requested space aligned on 16 bytes to zero
|
|
* pad the display. */
|
|
buffer = calloc(1, (size + 15) & ~15);
|
|
memcpy(buffer, syscfgData + entry.seDataOffset, size);
|
|
|
|
// Calculate the extents of the offset section
|
|
if (entry.seDataOffset < lowExt) {
|
|
lowExt = entry.seDataOffset;
|
|
}
|
|
if ((entry.seDataOffset + entry.seDataSize) > highExt) {
|
|
highExt = entry.seDataOffset + entry.seDataSize;
|
|
}
|
|
isExt = true;
|
|
} else {
|
|
/* allocate enough space to mirror the entrys buffer */
|
|
size = sizeof(entry.seData);
|
|
|
|
buffer = malloc(sizeof(entry.seData));
|
|
memcpy(buffer, entry.seData, size);
|
|
|
|
isExt = false;
|
|
}
|
|
printf("0x%08x '%c%c%c%c' %4dB %c ",
|
|
entry.seTag,
|
|
(entry.seTag >> 24) & 0xff,
|
|
(entry.seTag >> 16) & 0xff,
|
|
(entry.seTag >> 8) & 0xff,
|
|
entry.seTag & 0xff,
|
|
entry.seDataSize,
|
|
isExt ? '*' : ' ');
|
|
|
|
/* Format the data in nice columns */
|
|
for (i = 0; i < size; i += 16 ) {
|
|
printf("%*s", (i >= 16) ? 28 : 0, "");
|
|
|
|
/* Starting at the offset, display the data values up to 4 per line. */
|
|
for (j = i; j < (i + 16); j += 4) {
|
|
printf("0x%08x ", *((u_int32_t *)&buffer[j]));
|
|
}
|
|
|
|
printf("%s\n", sfx);
|
|
}
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
printf("\n");
|
|
printf("header @ 0-%zu", sizeof(struct syscfgHeader) - 1);
|
|
printf("; entries @ %zu-%d", sizeof(struct syscfgHeader), hdr.shSize - 1);
|
|
if (lowExt < highExt) {
|
|
extSize = highExt - lowExt;
|
|
printf("; extra data @ %zu-%zu", lowExt, highExt - 1);
|
|
}
|
|
printf("; using %zu of %d bytes\n", hdr.shSize + extSize, hdr.shMaxSize);
|
|
|
|
return(0);
|
|
}
|
|
|
|
bool
|
|
syscfgInitWithBdev(const char *bdevName)
|
|
{
|
|
int result;
|
|
struct blockdev *candidate;
|
|
struct syscfgHeader hdr;
|
|
|
|
if (syscfgData != NULL)
|
|
return(false);
|
|
|
|
/* look for the suggested bdev */
|
|
if ((candidate = lookup_blockdev(bdevName)) == NULL) {
|
|
dprintf(DEBUG_INFO, "syscfg: can't find bdev \"%s\"\n", bdevName);
|
|
return(false);
|
|
}
|
|
|
|
/* Make sure it's big enough */
|
|
if (candidate->total_len <= kSysCfgBdevOffset + sizeof(hdr)) {
|
|
dprintf(DEBUG_INFO, "syscfg: bdev size 0x%llx too small\n", candidate->total_len);
|
|
return (false);
|
|
}
|
|
|
|
/* look for the header */
|
|
if (blockdev_read(candidate, &hdr, kSysCfgBdevOffset, sizeof(hdr)) < (int)sizeof(hdr)) {
|
|
dprintf(DEBUG_INFO, "syscfg: bdev read fail\n");
|
|
return(false);
|
|
}
|
|
|
|
if (hdr.shMagic != kSysCfgHeaderMagic) {
|
|
dprintf(DEBUG_INFO, "syscfg: bad magic\n");
|
|
return(false);
|
|
}
|
|
|
|
dprintf(DEBUG_CRITICAL, "syscfg: version 0x%08x with %d entries using %d of %d bytes\n",
|
|
hdr.shVersion, hdr.shKeyCount, hdr.shSize, hdr.shMaxSize);
|
|
|
|
/* If there is a syscfg there will also be diag info... protect them. */
|
|
blockdev_set_protection(candidate, 0x2000, 0x6000);
|
|
|
|
syscfgDataLength = candidate->total_len - kSysCfgBdevOffset;
|
|
if (hdr.shSize < syscfgDataLength)
|
|
syscfgDataLength = hdr.shMaxSize;
|
|
|
|
syscfgData = malloc(syscfgDataLength);
|
|
|
|
result = blockdev_read(candidate, syscfgData, kSysCfgBdevOffset, syscfgDataLength);
|
|
|
|
if (result < 0 || (size_t)result < syscfgDataLength) {
|
|
dprintf(DEBUG_INFO, "syscfg: bdev read fail (%d)\n", result);
|
|
syscfg_reinit();
|
|
return false;
|
|
}
|
|
|
|
syscfgKeyCount = hdr.shKeyCount;
|
|
|
|
return(true);
|
|
}
|
|
|
|
void *
|
|
syscfgGetData(struct syscfgMemEntry *entry)
|
|
{
|
|
/* Handle data stored externally from the entry */
|
|
if (entry->seDataOffset != 0) {
|
|
if (entry->seDataOffset > syscfgDataLength)
|
|
return NULL;
|
|
if (entry->seDataOffset + entry->seDataSize > syscfgDataLength)
|
|
return NULL;
|
|
|
|
return syscfgData + entry->seDataOffset;
|
|
} else {
|
|
return entry->seData;
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
syscfgGetSize(struct syscfgMemEntry *entry)
|
|
{
|
|
return entry->seDataSize;
|
|
}
|
|
|
|
int
|
|
syscfgCopyDataForTag(u_int32_t tag, u_int8_t *buffer, size_t size)
|
|
{
|
|
bool result;
|
|
struct syscfgMemEntry entry;
|
|
void *data;
|
|
|
|
result = syscfgFindByTag(tag, &entry);
|
|
if (!result)
|
|
return(-1);
|
|
if (size > entry.seDataSize)
|
|
size = entry.seDataSize;
|
|
|
|
data = syscfgGetData(&entry);
|
|
if (data == NULL)
|
|
return(-1);
|
|
|
|
memcpy(buffer, data, size);
|
|
|
|
// XXX check what to do with the assert that was removed from here
|
|
return size;
|
|
}
|
|
|
|
bool
|
|
syscfgFindByTag(u_int32_t tag, struct syscfgMemEntry *entry)
|
|
{
|
|
static struct syscfgMemEntry temp;
|
|
bool result;
|
|
u_int32_t index;
|
|
|
|
for (index = 0; index < syscfgKeyCount; index++) {
|
|
result = syscfgFindByIndex(index, &temp);
|
|
if (result && (temp.seTag == tag)) {
|
|
memcpy(entry, &temp, sizeof(*entry));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
syscfgFindByIndex(u_int32_t index, struct syscfgMemEntry *result)
|
|
{
|
|
struct syscfgEntry entry;
|
|
struct syscfgEntryCNTB *entryCNTB;
|
|
|
|
if (index >= syscfgKeyCount)
|
|
return false;
|
|
|
|
size_t offset = sizeof(struct syscfgHeader) + index * sizeof(struct syscfgEntry);
|
|
if (offset >= syscfgDataLength || offset + sizeof(struct syscfgEntry) >= syscfgDataLength)
|
|
return false;
|
|
|
|
memcpy(&entry, syscfgData + offset, sizeof(entry));
|
|
|
|
if (entry.seTag == 'CNTB') {
|
|
entryCNTB = (struct syscfgEntryCNTB *)&entry;
|
|
result->seTag = entryCNTB->seRealTag;
|
|
memset(result->seData, 0, sizeof(result->seData));
|
|
result->seDataSize = entryCNTB->seDataSize;
|
|
result->seDataOffset = entryCNTB->seDataOffset;
|
|
} else {
|
|
result->seTag = entry.seTag;
|
|
memcpy(result->seData, entry.seData, sizeof(result->seData));
|
|
result->seDataSize = sizeof(result->seData);
|
|
result->seDataOffset = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
syscfg_find_tag(uint32_t tag, void **data_out, uint32_t *size_out)
|
|
{
|
|
static struct syscfgMemEntry result;
|
|
|
|
if (syscfgFindByTag(tag, &result)) {
|
|
if (data_out) {
|
|
*data_out = syscfgGetData(&result);
|
|
}
|
|
if (size_out) {
|
|
*size_out = syscfgGetSize(&result);
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|