first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2014 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.
|
||||
*/
|
||||
#include <sys.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <debug.h>
|
||||
#include <lib/blockdev.h>
|
||||
#ifndef HOST_TEST
|
||||
#include <lib/mib.h>
|
||||
#endif
|
||||
#include <lib/libiboot.h>
|
||||
#include <limits.h>
|
||||
|
||||
static struct blockdev *bdev_list = NULL;
|
||||
|
||||
#define BLOCKSIZE_MOD(dev, val) ((val) & ((1<<((dev)->block_shift)) - 1))
|
||||
#define BLOCK_FROM_OFF(dev, off) ((off) >> (dev)->block_shift)
|
||||
#define BLOCK_TO_OFF(dev, block) ((block) << (dev)->block_shift)
|
||||
#define ALIGNMENT_MOD(dev, val) ((val) & ((1<<((dev)->alignment_shift)) - 1))
|
||||
|
||||
static size_t get_cpu_cache_line_size(void)
|
||||
{
|
||||
#ifndef HOST_TEST
|
||||
return mib_get_size(kMIBPlatformCacheLineSize);
|
||||
#elif defined CPU_CACHELINE_SIZE
|
||||
return CPU_CACHELINE_SIZE;
|
||||
#else
|
||||
#error "Cache line size is not defined"
|
||||
#endif
|
||||
}
|
||||
|
||||
struct blockdev *lookup_blockdev(const char *name)
|
||||
{
|
||||
struct blockdev *dev;
|
||||
|
||||
for (dev = bdev_list; dev; dev = dev->next) {
|
||||
if (strcmp(dev->name, name) == 0)
|
||||
return dev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct blockdev *first_blockdev(void)
|
||||
{
|
||||
return bdev_list;
|
||||
}
|
||||
|
||||
struct blockdev *next_blockdev(struct blockdev *dev)
|
||||
{
|
||||
return dev->next;
|
||||
}
|
||||
|
||||
int register_blockdev(struct blockdev *dev)
|
||||
{
|
||||
ASSERT(dev != NULL);
|
||||
ASSERT(dev->block_size > 0);
|
||||
|
||||
dprintf(DEBUG_INFO, "register_blockdev: dev %p '%s'\n", dev, dev->name);
|
||||
|
||||
dev->next = bdev_list;
|
||||
bdev_list = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *get_bounce_buf(struct blockdev *dev)
|
||||
{
|
||||
void *result;
|
||||
uint32_t align = __max(dev->alignment, (uint32_t)get_cpu_cache_line_size());
|
||||
uint32_t size = __max(dev->alignment, dev->block_size);
|
||||
|
||||
posix_memalign(&result, align, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* a reasonable default bytewise read routine in case the block device does not
|
||||
* choose to override the read_hook. deblocks the device by reading from the read_block hook.
|
||||
*/
|
||||
static int blockdev_read_hook(struct blockdev *dev, void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
int err;
|
||||
size_t bytes_read;
|
||||
block_addr block;
|
||||
size_t buf_offset;
|
||||
char *buf = NULL;
|
||||
|
||||
/* trim the read len */
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
if ((uint64_t)offset >= dev->total_len)
|
||||
return 0;
|
||||
if ((offset + len) > dev->total_len)
|
||||
len = dev->total_len - offset;
|
||||
|
||||
buf_offset = 0;
|
||||
bytes_read = 0;
|
||||
block = BLOCK_FROM_OFF(dev, offset);
|
||||
|
||||
// dprintf(DEBUG_INFO, "read: ptr %p, buf_offset %d, bytes_read %zd, block %u, len %d, offset %lld\n", ptr, buf_offset, bytes_read, block, len, offset);
|
||||
|
||||
/* read in partial first block */
|
||||
if (BLOCKSIZE_MOD(dev, offset) != 0 || ALIGNMENT_MOD(dev, (uintptr_t)ptr) != 0) {
|
||||
size_t block_off = BLOCKSIZE_MOD(dev, offset);
|
||||
size_t toread = __min(len, dev->block_size - block_off);
|
||||
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
/* copy the partial data */
|
||||
memcpy(ptr, buf + block_off, toread);
|
||||
|
||||
block++;
|
||||
offset += toread;
|
||||
buf_offset += toread;
|
||||
bytes_read += toread;
|
||||
len -= toread;
|
||||
}
|
||||
|
||||
// dprintf(DEBUG_INFO, "read2: ptr %p, buf_offset %d, bytes_read %zd, block %u, len %d, offset %lld, blockshift %d\n", ptr, buf_offset, bytes_read, block, len, offset, dev->block_shift);
|
||||
|
||||
ASSERT(len == 0 || BLOCKSIZE_MOD(dev, offset) == 0);
|
||||
|
||||
/* handle multiple middle blocks */
|
||||
while (len >= dev->block_size) {
|
||||
size_t blocks_to_read;
|
||||
size_t alignment_offset;
|
||||
|
||||
blocks_to_read = BLOCK_FROM_OFF(dev, len);
|
||||
|
||||
/* If the buffer we're writing to isn't aligned, shorten the read by a block and
|
||||
bounce through an aligned address higher up in the buffer. This relies on the block
|
||||
size being >= the alignment, which is enforced by blockdev_set_buffer_alignment.
|
||||
For reads of just one block, go through the bounce buffer */
|
||||
alignment_offset = ALIGNMENT_MOD(dev, (uintptr_t)ptr + buf_offset);
|
||||
if (alignment_offset != 0) {
|
||||
if (blocks_to_read > 1) {
|
||||
uint8_t *dest;
|
||||
dest = (uint8_t *)ptr + buf_offset + dev->alignment - alignment_offset;
|
||||
|
||||
err = blockdev_read_block(dev, dest, block, blocks_to_read - 1);
|
||||
if (err > 0)
|
||||
memmove((uint8_t *)ptr + buf_offset, dest, BLOCK_TO_OFF(dev, err));
|
||||
} else {
|
||||
if (!buf)
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err > 0)
|
||||
memmove((uint8_t *)ptr + buf_offset, buf, BLOCK_TO_OFF(dev, 1));
|
||||
}
|
||||
} else {
|
||||
err = blockdev_read_block(dev, (uint8_t *)ptr + buf_offset, block, blocks_to_read);
|
||||
}
|
||||
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
/* account for blocks read */
|
||||
block += err;
|
||||
buf_offset += BLOCK_TO_OFF(dev, err);
|
||||
bytes_read += BLOCK_TO_OFF(dev, err);
|
||||
len -= BLOCK_TO_OFF(dev, err);
|
||||
}
|
||||
|
||||
// dprintf(DEBUG_INFO, "read3: ptr %p, buf_offset %d, bytes_read %zd, block %ud, len %d, offset %lld\n", ptr, buf_offset, bytes_read, block, len, offset);
|
||||
|
||||
/* handle partial last block */
|
||||
if (len > 0) {
|
||||
ASSERT(len < dev->block_size);
|
||||
|
||||
if (!buf)
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
memcpy((uint8_t *)ptr + buf_offset, buf, len);
|
||||
|
||||
bytes_read += len;
|
||||
}
|
||||
|
||||
done:
|
||||
if (NULL != buf)
|
||||
free(buf);
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
|
||||
static int blockdev_read_block_hook(struct blockdev *dev, void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "no reasonable default block read routine\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int blockdev_write_hook(struct blockdev *dev, const void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
int err;
|
||||
size_t bytes_written;
|
||||
block_addr block;
|
||||
size_t buf_offset;
|
||||
char *buf = NULL;
|
||||
const void *src;
|
||||
|
||||
/* trim the write len */
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
if ((uint64_t)offset >= dev->total_len)
|
||||
return 0;
|
||||
if ((offset + len) > dev->total_len)
|
||||
len = dev->total_len - offset;
|
||||
|
||||
buf_offset = 0;
|
||||
bytes_written = 0;
|
||||
block = BLOCK_FROM_OFF(dev, offset);
|
||||
|
||||
// dprintf(DEBUG_INFO, "write: ptr %p, buf_offset %d, bytes_written %zd, block %u, len %d, offset %lld\n", ptr, buf_offset, bytes_written, block, len, offset);
|
||||
|
||||
/* write partial first block */
|
||||
if (BLOCKSIZE_MOD(dev, offset) != 0 || ALIGNMENT_MOD(dev, offset) != 0) {
|
||||
size_t block_off = BLOCKSIZE_MOD(dev, offset);
|
||||
size_t towrite = __min(len, dev->block_size - block_off);
|
||||
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
/* read in a buffer */
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
/* copy the partial data */
|
||||
memcpy(buf + block_off, ptr, towrite);
|
||||
|
||||
/* write it back out */
|
||||
err = blockdev_write_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
block++;
|
||||
offset += towrite;
|
||||
buf_offset += towrite;
|
||||
bytes_written += towrite;
|
||||
len -= towrite;
|
||||
}
|
||||
|
||||
// dprintf(DEBUG_INFO, "write2: ptr %p, buf_offset %d, bytes_written %zd, block %u, len %d, offset %lld, blockshift %d\n", ptr, buf_offset, bytes_written, block, len, offset, dev->block_shift);
|
||||
|
||||
ASSERT(len == 0 || BLOCKSIZE_MOD(dev, offset) == 0);
|
||||
|
||||
/* handle multiple middle blocks */
|
||||
while (len >= dev->block_size) {
|
||||
size_t blocks_to_write;
|
||||
size_t alignment_offset;
|
||||
|
||||
/* If the buffer we're reading from isn't aligned, bounce buffer each block. iBoot
|
||||
doesn't care about write performance for big writes, so it's ok for this to be slow */
|
||||
alignment_offset = ALIGNMENT_MOD(dev, (uintptr_t)ptr + buf_offset);
|
||||
if (alignment_offset != 0) {
|
||||
if (!buf)
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
blocks_to_write = 1;
|
||||
memcpy(buf, (const uint8_t *)ptr + buf_offset, BLOCK_TO_OFF(dev, blocks_to_write));
|
||||
src = buf;
|
||||
} else {
|
||||
blocks_to_write = BLOCK_FROM_OFF(dev, len);
|
||||
src = (const uint8_t *)ptr + buf_offset;
|
||||
}
|
||||
|
||||
err = blockdev_write_block(dev, src, block, blocks_to_write);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
/* account for write */
|
||||
block += err;
|
||||
buf_offset += BLOCK_TO_OFF(dev, err);
|
||||
bytes_written += BLOCK_TO_OFF(dev, err);
|
||||
len -= BLOCK_TO_OFF(dev, err);
|
||||
}
|
||||
|
||||
// dprintf(DEBUG_INFO, "write3: ptr %p, buf_offset %d, bytes_written %zd, block %ud, len %d, offset %lld\n", ptr, buf_offset, bytes_written, block, len, offset);
|
||||
|
||||
/* handle partial last block */
|
||||
if (len > 0) {
|
||||
ASSERT(len < dev->block_size);
|
||||
|
||||
if (!buf)
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
memcpy(buf, (uint8_t *)ptr + buf_offset, len);
|
||||
|
||||
err = blockdev_write_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
bytes_written += len;
|
||||
}
|
||||
|
||||
done:
|
||||
if (NULL != buf)
|
||||
free(buf);
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
static int blockdev_write_block_hook(struct blockdev *dev, const void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "no reasonable default block write routine\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int blockdev_erase_hook(struct blockdev *dev, off_t offset, uint64_t len)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "no reasonable default erase routine\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* compare memory with a range of the block device */
|
||||
int blockdev_compare(struct blockdev *dev, const void *_ptr, off_t bdev_offset, uint64_t len)
|
||||
{
|
||||
int result;
|
||||
int err;
|
||||
uint32_t i;
|
||||
uint8_t *buf;
|
||||
const uint8_t *ptr = (const uint8_t *)_ptr;
|
||||
|
||||
posix_memalign((void **)&buf, get_cpu_cache_line_size(), dev->block_size);
|
||||
|
||||
result = 0;
|
||||
while (len > 0) {
|
||||
size_t toread = __min(dev->block_size, len);
|
||||
|
||||
err = blockdev_read(dev, buf, bdev_offset, toread);
|
||||
if (err <= 0) {
|
||||
dprintf(DEBUG_CRITICAL, "bdev_compare: error reading from device at offset %lld\n", bdev_offset);
|
||||
result = -1;
|
||||
goto out;
|
||||
}
|
||||
if ((uint32_t)err < toread) {
|
||||
dprintf(DEBUG_CRITICAL, "bdev_compare: short read from device at offset %lld (read %d)\n", bdev_offset, err);
|
||||
result = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i=0; i < toread; i++) {
|
||||
if (buf[i] != *ptr) {
|
||||
dprintf(DEBUG_CRITICAL, "difference at offset %lld: dev 0x%02x mem 0x%02x\n", bdev_offset + i, buf[i], *ptr);
|
||||
result++;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
len -= toread;
|
||||
bdev_offset += toread;
|
||||
}
|
||||
|
||||
out:
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* write to the bdev safely, avoiding the protected region */
|
||||
int blockdev_write_protected(struct blockdev *dev, const void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
size_t overlap;
|
||||
off_t protect_start, protect_end;
|
||||
uint64_t total_length;
|
||||
int err;
|
||||
off_t bytes_written = 0;
|
||||
|
||||
// printf("write_protected: ptr %p offset 0x%llx length 0x%x\n", ptr, offset, len);
|
||||
protect_start = dev->protect_start;
|
||||
protect_end = dev->protect_end;
|
||||
total_length = (uint64_t)offset + len;
|
||||
// Test for negative offset and overlap
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
RELEASE_ASSERT(total_length >= len);
|
||||
|
||||
/* write the portion before the protected region */
|
||||
if (offset < protect_start) {
|
||||
ASSERT(protect_start >= 0);
|
||||
if ((offset + len) > (size_t)protect_start) {
|
||||
overlap = (offset + len) - protect_start;
|
||||
} else {
|
||||
overlap = 0;
|
||||
}
|
||||
// printf("BDEV: writing first part: 0x%llx/0x%x\n", offset, len - overlap);
|
||||
err = dev->write_hook(dev, ptr, offset, len - overlap);
|
||||
if (err < 0)
|
||||
return err;
|
||||
bytes_written += err;
|
||||
}
|
||||
|
||||
/* and write the portion after */
|
||||
RELEASE_ASSERT(protect_end >= 0);
|
||||
if ((offset + len) > (size_t)protect_end) {
|
||||
if (offset < protect_end) {
|
||||
overlap = protect_end - offset;
|
||||
} else {
|
||||
overlap = 0;
|
||||
}
|
||||
// printf("BDEV: writing second part: 0x%llx/0x%x\n", offset + overlap, len - overlap);
|
||||
err = dev->write_hook(dev, (char *)ptr + overlap, offset + overlap, len - overlap);
|
||||
if (err < 0)
|
||||
return err;
|
||||
bytes_written += err;
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
|
||||
int blockdev_write_block_protected(struct blockdev *dev, const void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
size_t overlap;
|
||||
size_t protect_start, protect_end;
|
||||
int err;
|
||||
uint32_t total_count;
|
||||
|
||||
// printf("write_block_protected: ptr %p block 0x%x count 0x%x\n", ptr, block, count);
|
||||
protect_start = dev->protect_start >> dev->block_shift;
|
||||
protect_end = dev->protect_end >> dev->block_shift;
|
||||
total_count = block + count;
|
||||
// Test for overlap
|
||||
RELEASE_ASSERT(total_count >= block);
|
||||
|
||||
/* write the portion before the protected region */
|
||||
if (block < protect_start) {
|
||||
if ((block + count) > protect_start) {
|
||||
overlap = (block + count) - protect_start;
|
||||
} else {
|
||||
overlap = 0;
|
||||
}
|
||||
// printf("BDEV: writing first part: 0x%x/0x%x\n", block, count - overlap);
|
||||
err = dev->write_block_hook(dev, ptr, block, count - overlap);
|
||||
if (err < 0) /* error */
|
||||
return(-1);
|
||||
if ((uint32_t)err < (count - overlap)) /* short write */
|
||||
return(err);
|
||||
|
||||
}
|
||||
|
||||
/* and write the portion after */
|
||||
if ((block + count) > protect_end) {
|
||||
if (block < protect_end) {
|
||||
overlap = protect_end - block;
|
||||
} else {
|
||||
overlap = 0;
|
||||
}
|
||||
// printf("BDEV: writing second part: 0x%x/0x%x\n", block + overlap, count - overlap);
|
||||
err = dev->write_block_hook(dev, (char *)ptr + (overlap << dev->block_shift), block + overlap, count - overlap);
|
||||
if (err < 0) /* error */
|
||||
return(-1);
|
||||
if ((uint32_t)err < (count - overlap)) /* short write XXX does not handle both ends
|
||||
* overhanging */
|
||||
return(err);
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
|
||||
/* set the protected region */
|
||||
int blockdev_set_protection(struct blockdev *dev, off_t offset, uint64_t length)
|
||||
{
|
||||
if (dev->protect_end == dev->protect_start) {
|
||||
dev->protect_start = offset;
|
||||
dev->protect_end = offset + length;
|
||||
dprintf(DEBUG_INFO, "BDEV: protecting 0x%llx-0x%llx\n", dev->protect_start, dev->protect_end);
|
||||
return(0);
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* set the required buffer alignment for read and write buffers */
|
||||
void blockdev_set_buffer_alignment(struct blockdev *dev, uint32_t alignment)
|
||||
{
|
||||
ASSERT(dev != NULL);
|
||||
|
||||
ASSERT(is_pow2(alignment));
|
||||
|
||||
/* Standard hooks don't support an alignment requirement bigger than the block size.
|
||||
Drivers that need this can just make their blocksize equal to the alignment and
|
||||
break the alignment-sized blocks into their intrinsic blocksize in their read block hook */
|
||||
ASSERT(alignment <= dev->block_size);
|
||||
|
||||
/* can't change the alignment after first setting it */
|
||||
ASSERT(dev->alignment == 1 && dev->alignment_shift == 0);
|
||||
|
||||
dev->alignment = alignment;
|
||||
dev->alignment_shift = log2_int(dev->alignment);
|
||||
}
|
||||
|
||||
/*
|
||||
* set up the shared part of the block device structure, and set the default read hooks
|
||||
* that provide a lot of the base functionality. The specific block device implementation should
|
||||
* customize the structure further
|
||||
*/
|
||||
int construct_blockdev(struct blockdev *dev, const char *name, uint64_t len, uint32_t block_size)
|
||||
{
|
||||
ASSERT(dev != NULL);
|
||||
ASSERT(is_pow2(block_size));
|
||||
ASSERT(strlen(name) < sizeof(dev->name));
|
||||
|
||||
strlcpy(dev->name, name, sizeof(dev->name));
|
||||
dev->flags = 0;
|
||||
dev->block_size = block_size;
|
||||
dev->block_shift = log2_int(dev->block_size);
|
||||
dev->block_count = len >> dev->block_shift;
|
||||
dev->total_len = len;
|
||||
dev->alignment = 1;
|
||||
dev->alignment_shift = 0;
|
||||
|
||||
ASSERT((len >> dev->block_shift) < UINT_MAX);
|
||||
|
||||
dev->read_hook = &blockdev_read_hook;
|
||||
dev->read_block_hook = &blockdev_read_block_hook;
|
||||
dev->write_hook = &blockdev_write_hook;
|
||||
dev->write_block_hook = &blockdev_write_block_hook;
|
||||
dev->erase_hook = &blockdev_erase_hook;
|
||||
|
||||
dev->protect_start = 0;
|
||||
dev->protect_end = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2011, 2013-2016 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <lib/env.h>
|
||||
#include <lib/mib.h>
|
||||
#include <sys/menu.h>
|
||||
#include <drivers/usb/usb_debug.h>
|
||||
|
||||
#define USB_TRANSFER_BLOCKS (1000)
|
||||
|
||||
static int do_slurp (struct blockdev *dev, char *filename, const size_t block_offset) {
|
||||
int err = 0;
|
||||
off_t lba;
|
||||
off_t end;
|
||||
size_t buf_offset;
|
||||
const size_t buf_size = dev->block_size * USB_TRANSFER_BLOCKS;
|
||||
uint8_t * read_buf;
|
||||
uint8_t * base_buf;
|
||||
uintptr_t buf_physical;
|
||||
|
||||
|
||||
end = dev->total_len >> dev->block_shift;
|
||||
base_buf = malloc(buf_size);
|
||||
if (NULL == base_buf) {
|
||||
printf ("malloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
buf_physical = mem_static_map_physical((uintptr_t)base_buf);
|
||||
|
||||
printf("slurping lba %zu to %llu\n", block_offset, end);
|
||||
|
||||
buf_offset = 0;
|
||||
lba = block_offset;
|
||||
|
||||
while (lba < end) {
|
||||
read_buf = base_buf + buf_offset;
|
||||
*(UInt32*)read_buf = 0xa5a5a5a5; /* poison buffer */
|
||||
err = blockdev_read(dev, read_buf, lba << dev->block_shift, dev->block_size);
|
||||
if (err < 0){
|
||||
printf("blockdev_read returns %d on lba %llu\n\n", err, lba);
|
||||
// don't stop slurping
|
||||
}
|
||||
buf_offset += dev->block_size;
|
||||
++lba;
|
||||
if (buf_offset == buf_size) {
|
||||
/*
|
||||
* This issues writes over the USB pipe for USB_TRANSFER_BLOCKS ' worth of
|
||||
* device blocks. This could be (4096) * 1000 or (8192) * 1000 depending
|
||||
* on device block size.
|
||||
*/
|
||||
err = usb_send_data_to_file(filename, buf_offset, buf_physical, 0);
|
||||
if (err) {
|
||||
printf("USB transfer error %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
buf_offset = 0;
|
||||
printf("Completed to block offset %llu\r", lba);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send over the remainder of blocks.. */
|
||||
if (buf_offset > 0) {
|
||||
err = usb_send_data_to_file(filename, buf_offset, buf_physical, 0);
|
||||
if (err) {
|
||||
printf("USB transfer error %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
printf("Completed to block offset %llu with code %d\n", lba, err);
|
||||
|
||||
free(base_buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int do_blockdev(int argc, struct cmd_arg *args)
|
||||
{
|
||||
if (!security_allow_modes(kSecurityModeDebugCmd)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
printf("not enough arguments\n");
|
||||
usage:
|
||||
printf("usage:\n");
|
||||
printf("%s list\n", args[0].str);
|
||||
printf("%s read <dev> <offset> <len> [<address>]\n", args[0].str);
|
||||
printf("%s scan <dev> <lba> <num>\n", args[0].str);
|
||||
printf("%s slurp <dev> <file> \n", args[0].str);
|
||||
printf("%s write <dev> <offset> [<len>] [<address>]\n", args[0].str);
|
||||
printf("%s memcmp <dev> <offset> [<len>] [<address>]\n", args[0].str);
|
||||
printf("%s erase <dev> <offset> <len>\n", args[0].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(args[1].str, "list")) {
|
||||
struct blockdev *dev;
|
||||
|
||||
printf("block device list:\n");
|
||||
for (dev = first_blockdev(); dev; dev = next_blockdev(dev)) {
|
||||
printf("\tdevice '%s': size %lld, block_size %d\n", dev->name, dev->total_len, dev->block_size);
|
||||
}
|
||||
} else if (!strcmp(args[1].str, "read") || !strcmp(args[1].str, "write") || !strcmp(args[1].str, "memcmp")) {
|
||||
if (argc < 4) {
|
||||
printf("not enough arguments.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
off_t offset = args[3].u;
|
||||
size_t len;
|
||||
addr_t addr;
|
||||
int err;
|
||||
struct blockdev *dev;
|
||||
|
||||
if (argc >= 5) {
|
||||
len = args[4].u;
|
||||
} else {
|
||||
len = env_get_uint("filesize", 0);
|
||||
if (len == 0) {
|
||||
printf("filesize variable invalid or not set, aborting\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc >= 6) {
|
||||
addr = args[5].u;
|
||||
} else {
|
||||
addr = env_get_uint("loadaddr", mib_get_addr(kMIBTargetDefaultLoadAddress));
|
||||
}
|
||||
|
||||
if (!security_allow_memory((void *)addr, len)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev = lookup_blockdev(args[2].str);
|
||||
if (!dev) {
|
||||
printf("couldn't find block device '%s'\n", args[2].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(args[1].str, "read")) {
|
||||
printf("reading %zu bytes from device '%s' to address 0x%llx\n", len, args[2].str, (uint64_t)addr);
|
||||
err = blockdev_read(dev, (void *)addr, offset, len);
|
||||
printf("blockdev_read returns %d\n", err);
|
||||
} else if (!strcmp(args[1].str, "write")) {
|
||||
printf("writing %zu bytes to device '%s' from address 0x%llx\n", len, args[2].str, (uint64_t)addr);
|
||||
err = blockdev_write(dev, (void *)addr, offset, len);
|
||||
printf("blockdev_write returns %d\n", err);
|
||||
} else {
|
||||
printf("comparing %zu bytes from device '%s' to address 0x%llx\n", len, args[2].str, (uint64_t)addr);
|
||||
err = blockdev_compare(dev, (void *)addr, offset, len);
|
||||
printf("blockdev_compare returns %d\n", err);
|
||||
}
|
||||
} else if (!strcmp(args[1].str, "erase")) {
|
||||
if (argc < 5) {
|
||||
printf("not enough arguments.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
off_t offset = args[3].u;
|
||||
size_t len = args[4].u;
|
||||
int err;
|
||||
struct blockdev *dev;
|
||||
|
||||
dev = lookup_blockdev(args[2].str);
|
||||
if (!dev) {
|
||||
printf("couldn't find block device '%s'\n", args[2].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = blockdev_erase(dev, offset, len);
|
||||
printf("blockdev_erase returns %d\n", err);
|
||||
} else if (!strcmp(args[1].str, "scan")) {
|
||||
if (argc < 5) {
|
||||
printf("not enough arguments.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
off_t lba = (off_t)args[3].u;
|
||||
size_t len = args[4].u;
|
||||
int err;
|
||||
struct blockdev *dev;
|
||||
off_t end;
|
||||
uint8_t *buffer;
|
||||
|
||||
dev = lookup_blockdev(args[2].str);
|
||||
if (!dev) {
|
||||
printf("couldn't find block device '%s'\n", args[2].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!len || ((off_t)(lba + len) > (off_t)(dev->total_len >> dev->block_shift))){
|
||||
end = dev->total_len >> dev->block_shift;
|
||||
}else{
|
||||
end = lba + len;
|
||||
}
|
||||
|
||||
buffer = malloc(dev->block_size);
|
||||
|
||||
printf("scanning lba %llu to %llu\n", lba, end);
|
||||
while (lba < end)
|
||||
{
|
||||
*buffer = 0xa5;
|
||||
err = blockdev_read(dev, buffer, lba << dev->block_shift, dev->block_size);
|
||||
if (err < 0){
|
||||
printf("blockdev_read returns %d on lba %llu\n", err, lba);
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
if (lba % 1000 == 0)
|
||||
printf("lba %zu\r", (size_t)lba);
|
||||
++lba;
|
||||
}
|
||||
free(buffer);
|
||||
printf("blockdev scan completed with no errors\n");
|
||||
}
|
||||
else if (!strcmp(args[1].str, "slurp")) {
|
||||
/* Is it a slurp? */
|
||||
if (argc < 3) {
|
||||
/* bdev slurp device filename */
|
||||
printf("slurp invalid arguments \n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
struct blockdev *dev;
|
||||
size_t block_offset;
|
||||
|
||||
dev = lookup_blockdev(args[2].str);
|
||||
block_offset = 0;
|
||||
|
||||
if (!dev) {
|
||||
printf("could not find blk dev ' %s ' \n", args[2].str);
|
||||
goto usage;
|
||||
}
|
||||
|
||||
return do_slurp (dev, args[3].str, block_offset);
|
||||
|
||||
}
|
||||
else {
|
||||
printf("unrecognized subcommand.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LIBBLOCKDEV_DIR := $(GET_LOCAL_DIR)
|
||||
LIBBLOCKDEV_BUILD := $(call TOLIBDIR,$(LIBBLOCKDEV_DIR)/LIBBLOCKDEV.a)
|
||||
COMMONLIBS += LIBBLOCKDEV
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
LIBBLOCKDEV_OBJS := \
|
||||
$(LIBBLOCKDEV_DIR)/blockdev.o \
|
||||
$(LIBBLOCKDEV_DIR)/subdev.o \
|
||||
$(LIBBLOCKDEV_DIR)/mem_blockdev.o \
|
||||
$(LIBBLOCKDEV_DIR)/debug.o
|
||||
|
||||
LIBBLOCKDEV_OBJS := $(call TOLIBOBJDIR,$(LIBBLOCKDEV_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBBLOCKDEV_OBJS:%o=%d)
|
||||
|
||||
$(LIBBLOCKDEV_BUILD): $(LIBBLOCKDEV_OBJS)
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2011 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.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <sys.h>
|
||||
#include <string.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct mem_blockdev {
|
||||
struct blockdev bdev;
|
||||
|
||||
/* local data */
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
static int mem_blockdev_read_block(struct blockdev *bdev, void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct mem_blockdev *mdev = (struct mem_blockdev *)bdev;
|
||||
|
||||
// no overflow
|
||||
RELEASE_ASSERT(block + count >= block);
|
||||
|
||||
if (block >= mdev->bdev.block_count)
|
||||
return 0;
|
||||
if ((block + count) > mdev->bdev.block_count)
|
||||
count = mdev->bdev.block_count - block;
|
||||
|
||||
dprintf(DEBUG_SPEW, "mem_blockdev_read: dev %p, ptr %p, block %d, count %d\n", bdev, ptr, block, count);
|
||||
|
||||
memcpy(ptr, (uint8_t *)mdev->ptr + (block << mdev->bdev.block_shift), count << mdev->bdev.block_shift);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int mem_blockdev_read(struct blockdev *bdev, void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
struct mem_blockdev *mdev = (struct mem_blockdev *)bdev;
|
||||
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
// no overflow
|
||||
RELEASE_ASSERT((uint64_t)offset + len >= (uint64_t)offset);
|
||||
|
||||
if ((uint64_t)offset >= mdev->bdev.total_len)
|
||||
return 0;
|
||||
if ((offset + len) > mdev->bdev.total_len)
|
||||
len = mdev->bdev.total_len - offset;
|
||||
|
||||
dprintf(DEBUG_SPEW, "mem_blockdev_read: dev %p, ptr %p, off %lld, len %llu\n", bdev, ptr, offset, len);
|
||||
|
||||
memcpy(ptr, (uint8_t *)mdev->ptr + offset, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
struct blockdev *create_mem_blockdev(const char *name, void *ptr, uint64_t len, uint32_t block_size)
|
||||
{
|
||||
struct mem_blockdev *mdev;
|
||||
|
||||
mdev = malloc(sizeof(struct mem_blockdev));
|
||||
|
||||
construct_blockdev(&mdev->bdev, name, len, block_size);
|
||||
mdev->ptr = ptr;
|
||||
|
||||
mdev->bdev.read_hook = &mem_blockdev_read;
|
||||
mdev->bdev.read_block_hook = &mem_blockdev_read_block;
|
||||
|
||||
return &mdev->bdev;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2007, 2009-2014 Apple Inc. All rights reserved.
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple, Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
OPTIONS += WITH_BLOCKDEV=1
|
||||
LIBRARY_MODULES += lib/blockdev
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2011 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.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct subdev_blockdev {
|
||||
struct blockdev bdev;
|
||||
|
||||
/* local data */
|
||||
struct blockdev *parent_dev;
|
||||
off_t offset;
|
||||
};
|
||||
|
||||
static int subdev_blockdev_read(struct blockdev *bdev, void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
struct subdev_blockdev *sdev = (struct subdev_blockdev *)bdev;
|
||||
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
if ((uint64_t)offset >= sdev->bdev.total_len)
|
||||
return 0;
|
||||
if ((offset + len) > sdev->bdev.total_len)
|
||||
len = sdev->bdev.total_len - offset;
|
||||
|
||||
return blockdev_read(sdev->parent_dev, ptr, offset + sdev->offset, len);
|
||||
}
|
||||
|
||||
static int subdev_blockdev_read_block(struct blockdev *bdev, void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct subdev_blockdev *sdev = (struct subdev_blockdev *)bdev;
|
||||
|
||||
/* since our blocks might not be aligned on parent block boundaries, just punch through to the
|
||||
* arbitrary alignment read handler and let it deal with it
|
||||
*/
|
||||
return subdev_blockdev_read(bdev, ptr, block << sdev->bdev.block_shift, count << sdev->bdev.block_shift);
|
||||
}
|
||||
|
||||
static int subdev_blockdev_write(struct blockdev *bdev, const void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
struct subdev_blockdev *sdev = (struct subdev_blockdev *)bdev;
|
||||
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
if ((uint64_t)offset >= sdev->bdev.total_len)
|
||||
return 0;
|
||||
if ((offset + len) > sdev->bdev.total_len)
|
||||
len = sdev->bdev.total_len - offset;
|
||||
|
||||
return blockdev_write(sdev->parent_dev, ptr, offset + sdev->offset, len);
|
||||
}
|
||||
|
||||
static int subdev_blockdev_write_block(struct blockdev *bdev, const void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct subdev_blockdev *sdev = (struct subdev_blockdev *)bdev;
|
||||
|
||||
/* since our blocks might not be aligned on parent block boundaries, just punch through to the
|
||||
* arbitrary alignment write handler and let it deal with it
|
||||
*/
|
||||
return subdev_blockdev_write(bdev, ptr, block << sdev->bdev.block_shift, count << sdev->bdev.block_shift);
|
||||
}
|
||||
|
||||
struct blockdev *create_subdev_blockdev(const char *name, struct blockdev *parent, off_t off, uint64_t len, uint32_t block_size)
|
||||
{
|
||||
struct subdev_blockdev *dev;
|
||||
|
||||
ASSERT(parent);
|
||||
|
||||
dprintf(DEBUG_SPEW, "create_subdev: name '%s' parent '%s' off 0x%llx len 0x%llx bsize 0x%x\n",
|
||||
name, parent->name, off, len, block_size);
|
||||
|
||||
dev = malloc(sizeof(struct subdev_blockdev));
|
||||
|
||||
construct_blockdev(&dev->bdev, name, len, block_size);
|
||||
blockdev_set_buffer_alignment(&dev->bdev, parent->alignment);
|
||||
dev->parent_dev = parent;
|
||||
dev->offset = off;
|
||||
|
||||
dev->bdev.read_hook = &subdev_blockdev_read;
|
||||
dev->bdev.read_block_hook = &subdev_blockdev_read_block;
|
||||
dev->bdev.write_hook = &subdev_blockdev_write;
|
||||
dev->bdev.write_block_hook = &subdev_blockdev_write_block;
|
||||
|
||||
return (struct blockdev *)dev;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
#include <unittest.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct mock_blockdev {
|
||||
struct blockdev bdev;
|
||||
|
||||
/* local data */
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
static int mock_blockdev_read_block(struct blockdev *bdev, void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct mock_blockdev *mdev = (struct mock_blockdev *)bdev;
|
||||
|
||||
// no overflow
|
||||
TEST_ASSERT_GTE(block + count, block);
|
||||
|
||||
// buffer must be properly aligned
|
||||
TEST_ASSERT_EQ((uintptr_t)ptr & (bdev->alignment - 1), 0);
|
||||
|
||||
if (block >= mdev->bdev.block_count)
|
||||
return 0;
|
||||
if ((block + count) > mdev->bdev.block_count)
|
||||
count = mdev->bdev.block_count - block;
|
||||
|
||||
memcpy(ptr, (uint8_t *)mdev->ptr + (block << mdev->bdev.block_shift), count << mdev->bdev.block_shift);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int mock_blockdev_write_block(struct blockdev *bdev, const void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct mock_blockdev *mdev = (struct mock_blockdev *)bdev;
|
||||
|
||||
// no overflow
|
||||
TEST_ASSERT_GTE(block + count, block);
|
||||
|
||||
// buffer must be properly aligned
|
||||
TEST_ASSERT_EQ((uintptr_t)ptr & (bdev->alignment - 1), 0);
|
||||
|
||||
if (block >= mdev->bdev.block_count)
|
||||
return 0;
|
||||
if ((block + count) > mdev->bdev.block_count)
|
||||
count = mdev->bdev.block_count - block;
|
||||
|
||||
memcpy(mdev->ptr + (block << mdev->bdev.block_shift), ptr, count << mdev->bdev.block_shift);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
struct blockdev *create_mock_blockdev(const char *name, void *ptr, uint64_t len, uint32_t block_size)
|
||||
{
|
||||
struct mock_blockdev *mdev;
|
||||
|
||||
mdev = malloc(sizeof(struct mock_blockdev));
|
||||
|
||||
construct_blockdev(&mdev->bdev, name, len, block_size);
|
||||
mdev->ptr = ptr;
|
||||
|
||||
mdev->bdev.read_block_hook = &mock_blockdev_read_block;
|
||||
mdev->bdev.write_block_hook = &mock_blockdev_write_block;
|
||||
|
||||
return &mdev->bdev;
|
||||
}
|
||||
|
||||
static void do_blockdev_read_tests(struct blockdev *bdev, uint8_t *backing, size_t backing_size, uint32_t block_size)
|
||||
{
|
||||
uint8_t *readback;
|
||||
uint8_t *zeroes;
|
||||
int result;
|
||||
|
||||
readback = malloc(backing_size + 1);
|
||||
TEST_ASSERT_NOT_NULL(readback);
|
||||
zeroes = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(zeroes);
|
||||
|
||||
memset(zeroes, 0, backing_size);
|
||||
|
||||
// Reading entire buffer should work
|
||||
memset(readback, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback, 0, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size);
|
||||
TEST_ASSERT_MEM_EQ(backing, readback, backing_size);
|
||||
|
||||
// Reading entire buffer unaligned should work
|
||||
memset(readback + 1, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback + 1, 0, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size);
|
||||
TEST_ASSERT_MEM_EQ(backing, readback + 1, backing_size);
|
||||
|
||||
// Reading past the end results in a short read
|
||||
memset(readback, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback, backing_size / 2, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size / 2);
|
||||
// first half of buffer should have result of read
|
||||
TEST_ASSERT_MEM_EQ(readback, backing + backing_size / 2, backing_size / 2);
|
||||
// second half should be untouched
|
||||
TEST_ASSERT_MEM_EQ(readback + backing_size / 2, zeroes, backing_size / 2);
|
||||
|
||||
// Reading totally past the end results in no read
|
||||
memset(readback, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback, backing_size + 2, backing_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
// buffer should be totally untouched
|
||||
TEST_ASSERT_MEM_EQ(readback, zeroes, backing_size);
|
||||
|
||||
// Reads that are weird size should work
|
||||
uint32_t read_sizes[] = {1, 2, 3, 13, 127, 129, 1023, 1025, 4094, 4097, block_size, block_size - 1, block_size - 2, backing_size - 2, backing_size - 1};
|
||||
for (uint32_t i = 0; i < sizeof(read_sizes) / sizeof(read_sizes[0]); i++) {
|
||||
uint32_t read_size = read_sizes[i];
|
||||
if (read_size == 0 || read_size >= backing_size)
|
||||
continue;
|
||||
|
||||
uint32_t read_offsets[] = {0, 1, 2, 3, 13, 127, 129, 1023, 1025, 4094, 4097, block_size, block_size - 1, block_size - 2, backing_size - 2, backing_size - 1};
|
||||
for (uint32_t j = 0; j < sizeof(read_offsets) / sizeof(read_offsets[0]); j++) {
|
||||
uint32_t read_offset = read_offsets[j];
|
||||
if (read_offset >= backing_size || read_offset + read_size >= backing_size)
|
||||
continue;
|
||||
tprintf(TEST_INFO, "doing offset=%u size=%u\n", read_offset, read_size);
|
||||
|
||||
// make sure the read succeeds
|
||||
memset(readback + 1, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback, read_offset, read_size);
|
||||
TEST_ASSERT_EQ(result, read_size);
|
||||
TEST_ASSERT_MEM_EQ(readback, backing + read_offset, read_size);
|
||||
TEST_ASSERT_MEM_EQ(readback + read_size, zeroes, backing_size - read_size);
|
||||
|
||||
// make sure read succeeds with alignment forced off
|
||||
memset(readback + 1, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback + 1, read_offset, read_size);
|
||||
TEST_ASSERT_EQ(result, read_size);
|
||||
TEST_ASSERT_MEM_EQ(readback + 1, backing + read_offset, read_size);
|
||||
TEST_ASSERT_MEM_EQ(readback + 1 + read_size, zeroes, backing_size - read_size);
|
||||
}
|
||||
}
|
||||
|
||||
free(readback);
|
||||
free(zeroes);
|
||||
}
|
||||
|
||||
static void do_blockdev_read_block_tests(struct blockdev *bdev, uint8_t *backing, size_t backing_size, uint32_t block_size)
|
||||
{
|
||||
uint8_t *readback;
|
||||
uint8_t *filler;
|
||||
int result;
|
||||
uint32_t blocks = backing_size / block_size;
|
||||
|
||||
readback = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(readback);
|
||||
filler = malloc(backing_size + 1);
|
||||
TEST_ASSERT_NOT_NULL(filler);
|
||||
|
||||
memset(filler, 0xee, backing_size);
|
||||
|
||||
// Reading entire buffer should work
|
||||
memcpy(readback, filler, backing_size);
|
||||
result = blockdev_read_block(bdev, readback, 0, blocks);
|
||||
TEST_ASSERT_EQ(result, blocks);
|
||||
TEST_ASSERT_MEM_EQ(backing, readback, backing_size);
|
||||
|
||||
// Reading past the end results in a short read
|
||||
memcpy(readback, filler, backing_size);
|
||||
result = blockdev_read_block(bdev, readback, blocks / 2, blocks);
|
||||
TEST_ASSERT_EQ(result, blocks / 2);
|
||||
// first half of buffer should have result of read
|
||||
TEST_ASSERT_MEM_EQ(readback, backing + backing_size / 2, backing_size / 2);
|
||||
// second half should be untouched
|
||||
TEST_ASSERT_MEM_EQ(readback + backing_size / 2, filler, backing_size / 2);
|
||||
|
||||
// Reading totally past the end results in no read
|
||||
memcpy(readback, filler, backing_size);
|
||||
result = blockdev_read_block(bdev, readback, blocks + 2, blocks);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
// buffer should be totally untouched
|
||||
TEST_ASSERT_MEM_EQ(readback, filler, backing_size);
|
||||
|
||||
free(readback);
|
||||
free(filler);
|
||||
}
|
||||
|
||||
void test_mem_blockdev(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(backing);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(backing + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
bdev = create_mem_blockdev("dev", backing, backing_size, block_size);
|
||||
|
||||
do_blockdev_read_tests(bdev, backing, backing_size, block_size);
|
||||
do_blockdev_read_block_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
// make sure backing wasn't touched during all the tests
|
||||
for (unsigned i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val;
|
||||
memcpy(&val, backing + i * sizeof(uint32_t), sizeof(uint32_t));
|
||||
TEST_ASSERT_EQ(val, i + 0xaabbccdd);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
void test_blockdev_read_block(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(backing);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(backing + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
bdev = create_mock_blockdev("dev1", backing, backing_size, block_size);
|
||||
do_blockdev_read_block_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
// make sure backing wasn't touched during all the tests
|
||||
for (unsigned i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val;
|
||||
memcpy(&val, backing + i * sizeof(uint32_t), sizeof(uint32_t));
|
||||
TEST_ASSERT_EQ(val, i + 0xaabbccdd);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
void test_blockdev_read(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(backing);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(backing + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
bdev = create_mock_blockdev("dev1", backing, backing_size, block_size);
|
||||
do_blockdev_read_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
// make sure backing wasn't touched during all the tests
|
||||
for (unsigned i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val;
|
||||
memcpy(&val, backing + i * sizeof(uint32_t), sizeof(uint32_t));
|
||||
TEST_ASSERT_EQ(val, i + 0xaabbccdd);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
void test_blockdev_set_buffer_alignment_read(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(backing);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(backing + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
bdev = create_mock_blockdev("dev1", backing, backing_size, block_size);
|
||||
blockdev_set_buffer_alignment(bdev, block_size);
|
||||
do_blockdev_read_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
if (block_size > 2) {
|
||||
bdev = create_mock_blockdev("dev2", backing, backing_size, block_size);
|
||||
blockdev_set_buffer_alignment(bdev, block_size / 2);
|
||||
do_blockdev_read_tests(bdev, backing, backing_size, block_size);
|
||||
}
|
||||
|
||||
// make sure backing wasn't touched during all the tests
|
||||
for (unsigned i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val;
|
||||
memcpy(&val, backing + i * sizeof(uint32_t), sizeof(uint32_t));
|
||||
TEST_ASSERT_EQ(val, i + 0xaabbccdd);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
static void do_blockdev_write_tests(struct blockdev *bdev, uint8_t *backing, size_t backing_size, uint32_t block_size)
|
||||
{
|
||||
uint8_t *data;
|
||||
uint8_t *filler;
|
||||
int result;
|
||||
|
||||
// need some source data we can write to the blockdev with
|
||||
// easily recognizable patterns
|
||||
data = malloc(backing_size + sizeof(uint32_t));
|
||||
TEST_ASSERT_NOT_NULL(data);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t) + 1; i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(data + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
filler = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(filler);
|
||||
|
||||
memset(filler, 0xee, backing_size);
|
||||
|
||||
// Writing entire buffer should work
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data, 0, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size);
|
||||
TEST_ASSERT_MEM_EQ(backing, data, backing_size);
|
||||
|
||||
// Writing entire buffer unaligned should work
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data + 1, 0, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size);
|
||||
TEST_ASSERT_MEM_EQ(backing, data + 1, backing_size);
|
||||
|
||||
// Writing past the end results in a short write
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data, backing_size / 2, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size / 2);
|
||||
// first half of backing should be untouched
|
||||
TEST_ASSERT_MEM_EQ(backing, filler, backing_size / 2);
|
||||
// second half of buffer should have result of write
|
||||
TEST_ASSERT_MEM_EQ(backing + backing_size / 2, data, backing_size / 2);
|
||||
|
||||
// Reading totally past the end results in no write
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data, backing_size + 2, backing_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
// buffer should be totally untouched
|
||||
TEST_ASSERT_MEM_EQ(backing, filler, backing_size);
|
||||
|
||||
// weird size writes should work
|
||||
uint32_t write_sizes[] = {1, 2, 3, 13, 127, 129, 1023, 1025, 4094, 4097, block_size, block_size - 1, block_size - 2, backing_size - 2, backing_size - 1};
|
||||
for (uint32_t i = 0; i < sizeof(write_sizes) / sizeof(write_sizes[0]); i++) {
|
||||
uint32_t write_size = write_sizes[i];
|
||||
if (write_size == 0 || write_size >= backing_size)
|
||||
continue;
|
||||
|
||||
uint32_t write_offsets[] = {0, 1, 2, 3, 13, 127, 129, 1023, 1025, 4094, 4097, block_size, block_size - 1, block_size - 2, backing_size - 2, backing_size - 1};
|
||||
for (uint32_t j = 0; j < sizeof(write_offsets) / sizeof(write_offsets[0]); j++) {
|
||||
uint32_t write_offset = write_offsets[j];
|
||||
if (write_offset >= backing_size || write_offset + write_size >= backing_size)
|
||||
continue;
|
||||
tprintf(TEST_INFO, "doing offset=%u size=%u\n", write_offset, write_size);
|
||||
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data, write_offset, write_size);
|
||||
TEST_ASSERT_EQ(result, write_size);
|
||||
TEST_ASSERT_MEM_EQ(backing + write_offset, data, write_size);
|
||||
// data before and after write should be untouched
|
||||
TEST_ASSERT_MEM_EQ(backing, filler, write_offset);
|
||||
TEST_ASSERT_MEM_EQ(backing + write_offset + write_size, filler, backing_size - write_size - write_offset);
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
free(filler);
|
||||
}
|
||||
|
||||
void test_blockdev_write(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
|
||||
bdev = create_mock_blockdev("dev1", backing, backing_size, block_size);
|
||||
do_blockdev_write_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
if (block_size > 1) {
|
||||
bdev = create_mock_blockdev("dev2", backing, backing_size, block_size);
|
||||
blockdev_set_buffer_alignment(bdev, block_size);
|
||||
do_blockdev_write_tests(bdev, backing, backing_size, block_size);
|
||||
}
|
||||
|
||||
if (block_size > 2) {
|
||||
bdev = create_mock_blockdev("dev3", backing, backing_size, block_size);
|
||||
blockdev_set_buffer_alignment(bdev, block_size / 2);
|
||||
do_blockdev_write_tests(bdev, backing, backing_size, block_size);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
void test_register_blockdev(uintptr_t param)
|
||||
{
|
||||
static uint8_t backing;
|
||||
struct blockdev *bdev0;
|
||||
struct blockdev *bdev1;
|
||||
uint32_t found = 0;
|
||||
|
||||
bdev0 = create_mock_blockdev("lookup-bdev0", &backing, 1, 1);
|
||||
TEST_ASSERT_NOT_NULL(bdev0);
|
||||
bdev1 = create_mock_blockdev("lookup-bdev1", &backing, 1, 1);
|
||||
TEST_ASSERT_NOT_NULL(bdev1);
|
||||
|
||||
TEST_ASSERT_NULL(lookup_blockdev("lookup-bdev0"));
|
||||
TEST_ASSERT_NULL(lookup_blockdev("lookup-bdev1"));
|
||||
|
||||
TEST_ASSERT_NULL(first_blockdev());
|
||||
|
||||
register_blockdev(bdev0);
|
||||
|
||||
TEST_ASSERT_PTR_EQ(lookup_blockdev("lookup-bdev0"), bdev0);
|
||||
TEST_ASSERT_PTR_EQ(first_blockdev(), bdev0);
|
||||
TEST_ASSERT_NULL(lookup_blockdev("lookup-bdev1"));
|
||||
|
||||
register_blockdev(bdev1);
|
||||
|
||||
TEST_ASSERT_PTR_EQ(lookup_blockdev("lookup-bdev0"), bdev0);
|
||||
TEST_ASSERT_PTR_EQ(lookup_blockdev("lookup-bdev1"), bdev1);
|
||||
|
||||
for (struct blockdev *bdev = first_blockdev(); bdev != NULL; bdev = next_blockdev(bdev)) {
|
||||
if (bdev == bdev0)
|
||||
found |= 1;
|
||||
if (bdev == bdev1)
|
||||
found |= 2;
|
||||
}
|
||||
TEST_ASSERT_EQ(found, 3);
|
||||
}
|
||||
|
||||
struct test_suite blockdev_suite = {
|
||||
.name = "blockdev",
|
||||
.test_cases = {
|
||||
{ "register_blockdev", test_register_blockdev, 0, "" },
|
||||
{ "mem_blockdev,1", test_mem_blockdev, 1, "" },
|
||||
{ "mem_blockdev,2", test_mem_blockdev, 2, "" },
|
||||
{ "mem_blockdev,4", test_mem_blockdev, 4, "" },
|
||||
{ "mem_blockdev,1024", test_mem_blockdev, 1024, "" },
|
||||
{ "blockdev_read_block,1", test_blockdev_read_block, 1, "" },
|
||||
{ "blockdev_read_block,2", test_blockdev_read_block, 2, "" },
|
||||
{ "blockdev_read_block,4", test_blockdev_read_block, 4, "" },
|
||||
{ "blockdev_read_block,1024", test_blockdev_read_block, 1024, "" },
|
||||
{ "blockdev_read_block,4096", test_blockdev_read_block, 4096, "" },
|
||||
{ "blockdev_read,1", test_blockdev_read, 1, "" },
|
||||
{ "blockdev_read,2", test_blockdev_read, 2, "" },
|
||||
{ "blockdev_read,4", test_blockdev_read, 4, "" },
|
||||
{ "blockdev_read,1024", test_blockdev_read, 1024, "" },
|
||||
{ "blockdev_read,4096", test_blockdev_read, 4096, "" },
|
||||
{ "blockdev_read,8192", test_blockdev_read, 8192, "" },
|
||||
{ "blockdev_write,1", test_blockdev_write, 1, "" },
|
||||
{ "blockdev_write,2", test_blockdev_write, 2, "" },
|
||||
{ "blockdev_write,4", test_blockdev_write, 4, "" },
|
||||
{ "blockdev_write,1024", test_blockdev_write, 1024, "" },
|
||||
{ "blockdev_write,4096", test_blockdev_write, 4096, "" },
|
||||
{ "blockdev_write,8192", test_blockdev_write, 8192, "" },
|
||||
{ "blockdev_set_buffer_alignment_read,2", test_blockdev_set_buffer_alignment_read, 2, "" },
|
||||
{ "blockdev_set_buffer_alignment_read,8", test_blockdev_set_buffer_alignment_read, 8, "" },
|
||||
{ "blockdev_set_buffer_alignment_read,1024", test_blockdev_set_buffer_alignment_read, 1024, "" },
|
||||
{ "blockdev_set_buffer_alignment_read,4096", test_blockdev_set_buffer_alignment_read, 4096, "" },
|
||||
TEST_CASE_LAST
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE(blockdev_suite);
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := blockdev
|
||||
|
||||
TEST_OBJS := \
|
||||
lib/libc/log2.o \
|
||||
$(LOCAL_DIR)/blockdev.o \
|
||||
$(LOCAL_DIR)/mem_blockdev.o \
|
||||
$(LOCAL_DIR)/subdev.o
|
||||
|
||||
TEST_SUPPORT_OBJS := \
|
||||
tests/unittest-main.o \
|
||||
$(LOCAL_DIR)/tests.o
|
||||
|
||||
TEST_CFLAGS := \
|
||||
-DCPU_CACHELINE_SIZE=32 \
|
||||
-DHOST_TEST=1
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2007, 2011, 2014 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <sys.h>
|
||||
#include <lib/heap.h>
|
||||
#include <lib/cbuf.h>
|
||||
#include <lib/libiboot.h>
|
||||
|
||||
#define INC_HEAD(cbuf) ((cbuf)->head = ((cbuf)->head + 1) & (cbuf)->len_mask)
|
||||
#define INC_TAIL(cbuf) ((cbuf)->tail = ((cbuf)->tail + 1) & (cbuf)->len_mask)
|
||||
|
||||
struct cbuf *cbuf_create(size_t len, struct task_event *event)
|
||||
{
|
||||
struct cbuf *buf;
|
||||
|
||||
if (!is_pow2(len))
|
||||
return NULL;
|
||||
|
||||
buf = malloc(sizeof(struct cbuf));
|
||||
|
||||
buf->buf = malloc(len);
|
||||
|
||||
buf->head = 0;
|
||||
buf->tail = 0;
|
||||
buf->has = 0;
|
||||
buf->len = len;
|
||||
buf->len_mask = len - 1;
|
||||
|
||||
buf->event = event;
|
||||
if (buf->event)
|
||||
event_init(buf->event, 0, false);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void cbuf_destroy(struct cbuf *buf)
|
||||
{
|
||||
if (buf == 0) return;
|
||||
|
||||
free(buf->buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
ssize_t cbuf_write_char(struct cbuf *buf, char c)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
/* if cbuf not valid, drop the character */
|
||||
if (buf == 0) return 1;
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
if (buf->has == buf->len) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
((char *)buf->buf)[buf->head] = c;
|
||||
INC_HEAD(buf);
|
||||
buf->has++;
|
||||
ret = 1;
|
||||
|
||||
if (buf->event)
|
||||
event_signal(buf->event);
|
||||
|
||||
out:
|
||||
exit_critical_section();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t cbuf_read_char(struct cbuf *buf, char *c)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
/* if cbuf not valid, return nothing */
|
||||
if (buf == 0) return 0;
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
if (buf->has == 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*c = ((char *)buf->buf)[buf->tail];
|
||||
INC_TAIL(buf);
|
||||
buf->has--;
|
||||
ret = 1;
|
||||
|
||||
out:
|
||||
if (buf->event && buf->has == 0)
|
||||
event_unsignal(buf->event);
|
||||
|
||||
exit_critical_section();
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LIBCBUF_DIR := $(GET_LOCAL_DIR)
|
||||
LIBCBUF_BUILD := $(call TOLIBDIR,$(LIBCBUF_DIR)/LIBCBUF.a)
|
||||
COMMONLIBS += LIBCBUF
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
GLOBAL_INCLUDES += $(SRCROOT)/arch/arm/include
|
||||
|
||||
LIBCBUF_OBJS := $(LIBCBUF_DIR)/cbuf.o
|
||||
|
||||
LIBCBUF_OBJS := $(call TOLIBOBJDIR,$(LIBCBUF_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBCBUF_OBJS:%o=%d)
|
||||
|
||||
$(LIBCBUF_BUILD): $(LIBCBUF_OBJS)
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
OPTIONS += WITH_CBUF=1
|
||||
LIBRARY_MODULES += lib/cbuf
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
* Copyright (C) 2007-2012 Apple 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 Inc.
|
||||
*/
|
||||
#include <sys.h>
|
||||
#include <arch.h>
|
||||
#include <sys/types.h>
|
||||
#include <lib/cksum.h>
|
||||
|
||||
#if (ARCH_ARMv7 && WITH_VFP) || defined(__arm64__)
|
||||
extern uint32_t adler32_vec(uint32_t, uint32_t, const uint8_t *, long);
|
||||
#endif
|
||||
|
||||
|
||||
#define BASE 65521L /* largest prime smaller than 65536 */
|
||||
#define NMAX 5000
|
||||
// NMAX (was 5521) the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
|
||||
|
||||
#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
|
||||
#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
|
||||
#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
|
||||
#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
|
||||
#define DO16(buf) DO8(buf,0); DO8(buf,8);
|
||||
|
||||
uint32_t adler32(const uint8_t *buf, long len)
|
||||
{
|
||||
uint32_t s1 = 1; // adler & 0xffff;
|
||||
uint32_t s2 = 0; // (adler >> 16) & 0xffff;
|
||||
int k;
|
||||
|
||||
#if (ARCH_ARMv7 && WITH_VFP) || defined(__arm64__)
|
||||
|
||||
/* align buf to 16-byte boundary */
|
||||
while ((int)buf & 15) { /* not on a 16-byte boundary */
|
||||
len--;
|
||||
s1 += *buf++;
|
||||
s2 += s1;
|
||||
s1 %= BASE;
|
||||
s2 %= BASE;
|
||||
}
|
||||
if (len > 16) {
|
||||
#if WITH_VFP_ALWAYS_ON
|
||||
return adler32_vec(s1, s2, buf, len);
|
||||
#else
|
||||
bool fp_enabled;
|
||||
uint32_t result;
|
||||
|
||||
fp_enabled = arch_task_fp_enable(true);
|
||||
result = adler32_vec(s1, s2, buf, len);
|
||||
arch_task_fp_enable(fp_enabled);
|
||||
|
||||
return result;
|
||||
#endif // WITH_VFP_ALWAYS_ON
|
||||
}
|
||||
#endif
|
||||
|
||||
while (len > 0) {
|
||||
k = len < NMAX ? len : NMAX;
|
||||
len -= k;
|
||||
while (k >= 16) {
|
||||
DO16(buf);
|
||||
buf += 16;
|
||||
k -= 16;
|
||||
}
|
||||
if (k != 0) do {
|
||||
s1 += *buf++;
|
||||
s2 += s1;
|
||||
} while (--k);
|
||||
s1 %= BASE;
|
||||
s2 %= BASE;
|
||||
}
|
||||
return (s2 << 16) | s1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Apple 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 Inc.
|
||||
*/
|
||||
|
||||
#if defined(__arm__)
|
||||
|
||||
#define BASE 65521 /* largest prime smaller than 65536 */
|
||||
#define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
|
||||
|
||||
// Note: buf should have been 16-byte aligned in the caller function,
|
||||
|
||||
// uLong adler32_vec(unsigned int adler, unsigned int sum2, const Bytef* buf, int len) {
|
||||
// unsigned n;
|
||||
// while (len >= NMAX) {
|
||||
// len -= NMAX;
|
||||
// n = NMAX / 16; /* NMAX is divisible by 16 */
|
||||
// do {
|
||||
// DO16(buf); /* 16 sums unrolled */
|
||||
// buf += 16;
|
||||
// } while (--n);
|
||||
// MOD(adler);
|
||||
// MOD(sum2);
|
||||
// }
|
||||
// if (len) { /* avoid modulos if none remaining */
|
||||
// while (len >= 16) {
|
||||
// len -= 16;
|
||||
// DO16(buf);
|
||||
// buf += 16;
|
||||
// }
|
||||
// while (len--) {
|
||||
// adler += *buf++;
|
||||
// sum2 += adler;
|
||||
// }
|
||||
// MOD(adler);
|
||||
// MOD(sum2);
|
||||
// }
|
||||
// return adler | (sum2 << 16); /* return recombined sums */
|
||||
// }
|
||||
|
||||
|
||||
/*
|
||||
DO16 vectorization:
|
||||
given initial unsigned int sum2 and adler, and a new set of 16 input bytes (x[0:15]), it can be shown that
|
||||
sum2 += (16*adler + 16*x[0] + 15*x[1] + ... + 1*x[15]);
|
||||
adler += (x[0] + x[1] + ... + x[15]);
|
||||
|
||||
therefore, this is what can be done to vectorize the above computation
|
||||
1. 16-byte aligned vector load into q2 (x[0:x15])
|
||||
2. sum2 += (adler<<4);
|
||||
3. vmull.u8 (q9,q8),q2,d2 where d2 = (1,1,1,1...,1), (q9,q8) + 16 16-bit elements x[0:15]
|
||||
4. vmull.u8 (q11,q10),q2,q0 where q0 = (1,2,3,4...,16), (q11,q10) + 16 16-bit elements (16:1)*x[0:15]
|
||||
5. parallel add (with once expansion to 32-bit) (q9,q8) and (q11,q10) all the way to accumulate to adler and sum2
|
||||
|
||||
In this revision, whenever possible, 2 DO16 loops are combined into a DO32 loop.
|
||||
1. 32-byte aligned vector load into q2,q14 (x[0:x31])
|
||||
2. sum2 += (adler<<5);
|
||||
3. vmull.u8 (4 q registers),(q2,q14),d2 where d2 = (1,1,1,1...,1), (4 q registers) : 32 16-bit elements x[0:31]
|
||||
4. vmull.u8 (4 q registers),(q2,q14),(q0,q15) where q0 = (1,...,32), (4 q regs) : 32 16-bit elements (32:1)*x[0:31]
|
||||
5. parallel add (with once expansion to 32-bit) the pair of (4 q regs) all the way to accumulate to adler and sum2
|
||||
|
||||
This change improves the performance by ~ 0.55 cycle/uncompress byte on ARM Cortex-A8.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
MOD implementation:
|
||||
adler%BASE = adler - floor(adler*(1/BASE))*BASE; where (1/BASE) = 0x80078071 in Q47
|
||||
1. vmull.u32 q2,(adler,sum2),(1/BASE) // *(1/BASE) in Q47
|
||||
2. vshr.u64 q2,q2,#47 // floor function
|
||||
3. vpadd.u32 d4,d4,d5 // merge into a double word in d4
|
||||
4. vmls.u32 (adler,sum2),d4,d3[0] // (adler,sum2) -= floor[(adler,sum2)/BASE]*BASE
|
||||
|
||||
*/
|
||||
|
||||
.text
|
||||
.align 2
|
||||
.globl _adler32_vec
|
||||
_adler32_vec:
|
||||
|
||||
#define adler r0
|
||||
#define sum2 r1
|
||||
#define buf r2
|
||||
#define len r3
|
||||
#define nmax r4
|
||||
#define vecs lr // vecs = NMAX/16
|
||||
#define n r5
|
||||
|
||||
#define t r12
|
||||
|
||||
#define sum2_coeff q0
|
||||
#define sum2_coeff0 d0
|
||||
#define sum2_coeff1 d1
|
||||
#define alder_coeff q1
|
||||
#define ones d2
|
||||
#define x0_x15 q2
|
||||
#define x0_x7 d4
|
||||
#define x8_x15 d5
|
||||
#define adlersum2 d6
|
||||
#define adler16 d25
|
||||
|
||||
adr t, vec_table // address to vec_table[]
|
||||
stmfd sp!, {r4, r5, lr}
|
||||
|
||||
vld1.32 {q0-q1},[t,:128]! // loading up coefficients for adler/sum2 computation
|
||||
vld1.32 {q15},[t,:128]! // for sum2 computation
|
||||
ldr nmax, [t] // NMAX
|
||||
|
||||
vmov adlersum2, sum2, adler // pack up adler/sum2 into a double register
|
||||
|
||||
cmp len, nmax // len vs NMAX
|
||||
lsr vecs, nmax, #4 // vecs = NMAX/16;
|
||||
blt L_len_lessthan_NMAX // if (len < NMAX) skip the while loop
|
||||
|
||||
sub len, nmax // pre-decrement len by NMAX
|
||||
|
||||
L_while_len_ge_NMAX_loop: // while (len>=NMAX) {
|
||||
|
||||
mov n, vecs, lsr #1 // n = NMAX/16;
|
||||
|
||||
L_do_loop: // do {
|
||||
|
||||
vshll.u32 q12, adlersum2, #5 // d25 = (0,32*adler) to be added into (adler,sum2)
|
||||
vld1.32 {x0_x15},[buf,:128]! // 16-byte input x0:x15
|
||||
vmull.u8 q8, x0_x7, ones // 16-bit x0-x7
|
||||
vld1.32 {q14}, [buf,:128]! // x16:x31
|
||||
vmull.u8 q9, x8_x15, ones // 16-bit x8-x15
|
||||
vadd.u32 adlersum2,adler16 // sum2 += old adler*32;
|
||||
vmull.u8 q12, d28, ones // 16-bit x16-x23
|
||||
vmull.u8 q13, d29, ones // 16-bit x24-x31
|
||||
vmull.u8 q10, d28, sum2_coeff0 // 16-bit x16*16, x17*15, ..., x23*9
|
||||
vmull.u8 q11, d29, sum2_coeff1 // 16-bit x24*8, x25*7, ..., x31*1
|
||||
vadd.u16 q8, q8, q9 // q8 = (x0+x8):(x7+x15) 8 16-bit elements for adler
|
||||
vmull.u8 q9, x0_x7, d30 // 16-bit x0*32,...,x7*25
|
||||
vmull.u8 q14, x8_x15, d31 // 16-bit x8*24,...,x15*17
|
||||
vadd.u16 q12, q12, q13 // q12 = (x16+x24):(x23+x31) 8 16-bit elements for adler
|
||||
vadd.u16 q10, q11 // 8 16-bit elements for sum2
|
||||
vadd.u16 q8, q12 // 8 16-bit elements for adler
|
||||
vadd.u16 q9, q14 // 8 16-bit elements for sum2
|
||||
vadd.u16 q10, q9 // 8 16-bit elements for sum2
|
||||
#if 0
|
||||
vpaddl.u16 q8, q8 // 4 32-bit elements for adler
|
||||
vpaddl.u16 q10, q10 // 4 32-bit elements for sum2
|
||||
vpadd.u32 d16,d16,d17 // 2 32-bit elements for adler
|
||||
vpadd.u32 d17,d20,d21 // 2 32-bit elements for sum2
|
||||
#else
|
||||
vadd.u16 d16, d17
|
||||
vadd.u16 d20, d21
|
||||
vpaddl.u16 d16, d16
|
||||
vpaddl.u16 d17, d20
|
||||
#endif
|
||||
vpadd.u32 d4,d17,d16 // s8 : 32-bit elements for sum2, s9 : 32-bit element for adler
|
||||
vadd.u32 adlersum2,d4 // update adler/sum2 with the new 16 bytes input
|
||||
|
||||
subs n, #1 // --n
|
||||
bgt L_do_loop // } while (--n);
|
||||
|
||||
vshll.u32 q12, adlersum2, #4 // d25 = (0,16*adler) to be added into (adler,sum2)
|
||||
|
||||
vld1.32 {x0_x15},[buf,:128]! // 16-byte input
|
||||
|
||||
vmull.u8 q8, x0_x7, ones // 16-bit x0-x7
|
||||
vmull.u8 q9, x8_x15, ones // 16-bit x8-x15
|
||||
vmull.u8 q10, x0_x7, sum2_coeff0 // 16-bit x0*16, x1*15, ..., x7*9
|
||||
vmull.u8 q11, x8_x15, sum2_coeff1 // 16-bit x8*8, x9*7, ..., x15*1
|
||||
|
||||
vadd.u16 q8, q8, q9 // 8 16-bit elements for adler
|
||||
vadd.u16 q10, q10, q11 // 8 16-bit elements for sum2
|
||||
#if 0
|
||||
vpaddl.u16 q8, q8 // 4 32-bit elements for adler
|
||||
vpaddl.u16 q10, q10 // 4 32-bit elements for sum2
|
||||
vpadd.u32 d16,d16,d17 // 2 32-bit elements for adler
|
||||
vpadd.u32 d17,d20,d21 // 2 32-bit elements for sum2
|
||||
#else
|
||||
vadd.u16 d16, d17
|
||||
vadd.u16 d20, d21
|
||||
vpaddl.u16 d16, d16
|
||||
vpaddl.u16 d17, d20
|
||||
|
||||
#endif
|
||||
vadd.u32 adlersum2,adler16 // sum2 += old adler;
|
||||
vpadd.u32 d4,d17,d16 // s8 : 32-bit elements for sum2, s9 : 32-bit element for adler
|
||||
vadd.u32 adlersum2,d4 // update adler/sum2 with the new 16 bytes input
|
||||
|
||||
// mod(alder,BASE); mod(sum2,BASE);
|
||||
vmull.u32 q2,adlersum2,d3[1] // alder/BASE, sum2/BASE in Q47
|
||||
vshr.u64 q2,q2,#47 // take the integer part
|
||||
vpadd.u32 d4,d4,d5 // merge into a double word in d4
|
||||
vmls.u32 adlersum2,d4,d3[0] // (adler,sum2) -= floor[(adler,sum2)/BASE]*BASE
|
||||
|
||||
subs len, nmax // len -= NMAX;
|
||||
bge L_while_len_ge_NMAX_loop // repeat while len >= NMAX
|
||||
|
||||
add len, nmax // post-increment len by NMAX
|
||||
|
||||
L_len_lessthan_NMAX:
|
||||
|
||||
cmp len, #0
|
||||
beq L_len_is_zero // if len==0, branch to skip the following
|
||||
|
||||
|
||||
subs len, #32 // pre-decrement len by 32
|
||||
blt L_len_lessthan_32 // if len < 32, branch to L_len16_loop
|
||||
|
||||
L_len32_loop:
|
||||
|
||||
vshll.u32 q12, adlersum2, #5 // d25 = (0,32*adler) to be added into (adler,sum2)
|
||||
vld1.32 {x0_x15},[buf,:128]! // 16-byte input x0:x15
|
||||
vmull.u8 q8, x0_x7, ones // 16-bit x0-x7
|
||||
vld1.32 {q14}, [buf,:128]! // x16:x31
|
||||
vmull.u8 q9, x8_x15, ones // 16-bit x8-x15
|
||||
vadd.u32 adlersum2,adler16 // sum2 += old adler*32;
|
||||
vmull.u8 q12, d28, ones // 16-bit x16-x23
|
||||
vmull.u8 q13, d29, ones // 16-bit x24-x31
|
||||
vmull.u8 q10, d28, sum2_coeff0 // 16-bit x16*16, x17*15, ..., x23*9
|
||||
vmull.u8 q11, d29, sum2_coeff1 // 16-bit x24*8, x25*7, ..., x31*1
|
||||
vadd.u16 q8, q8, q9 // q8 = (x0+x8):(x7+x15) 8 16-bit elements for adler
|
||||
vmull.u8 q9, x0_x7, d30 // 16-bit x0*32,...,x7*25
|
||||
vmull.u8 q14, x8_x15, d31 // 16-bit x8*24,...,x15*17
|
||||
vadd.u16 q12, q12, q13 // q12 = (x16+x24):(x23+x31) 8 16-bit elements for adler
|
||||
vadd.u16 q10, q11 // 8 16-bit elements for sum2
|
||||
vadd.u16 q8, q12 // 8 16-bit elements for adler
|
||||
vadd.u16 q9, q14 // 8 16-bit elements for sum2
|
||||
vadd.u16 q10, q9 // 8 16-bit elements for sum2
|
||||
#if 0
|
||||
vpaddl.u16 q8, q8 // 4 32-bit elements for adler
|
||||
vpaddl.u16 q10, q10 // 4 32-bit elements for sum2
|
||||
vpadd.u32 d16,d16,d17 // 2 32-bit elements for adler
|
||||
vpadd.u32 d17,d20,d21 // 2 32-bit elements for sum2
|
||||
#else
|
||||
vadd.u16 d16, d17
|
||||
vadd.u16 d20, d21
|
||||
vpaddl.u16 d16, d16
|
||||
vpaddl.u16 d17, d20
|
||||
#endif
|
||||
vpadd.u32 d4,d17,d16 // s8 : 32-bit elements for sum2, s9 : 32-bit element for adler
|
||||
vadd.u32 adlersum2,d4 // update adler/sum2 with the new 16 bytes input
|
||||
|
||||
subs len, #32 // len -= 32;
|
||||
bge L_len32_loop
|
||||
|
||||
L_len_lessthan_32:
|
||||
|
||||
adds len, #(32-16) // post-increment len by 32, then pre-decrement by 16
|
||||
blt L_len_lessthan_16 // if len < 16, branch to L_len_lessthan_16
|
||||
|
||||
vshll.u32 q12, adlersum2, #4 // d25 = (0,16*adler) to be added into (adler,sum2)
|
||||
|
||||
vld1.32 {x0_x15},[buf,:128]! // 16-byte input
|
||||
|
||||
|
||||
vmull.u8 q8, x0_x7, ones // 16-bit x0-x7
|
||||
vmull.u8 q9, x8_x15, ones // 16-bit x8-x15
|
||||
vmull.u8 q10, x0_x7, sum2_coeff0 // 16-bit x0*16, x1*15, ..., x7*9
|
||||
vmull.u8 q11, x8_x15, sum2_coeff1 // 16-bit x8*8, x9*7, ..., x15*1
|
||||
|
||||
vadd.u16 q8, q8, q9 // 8 16-bit elements for adler
|
||||
vadd.u16 q10, q10, q11 // 8 16-bit elements for sum2
|
||||
#if 0
|
||||
vpaddl.u16 q8, q8 // 4 32-bit elements for adler
|
||||
vpaddl.u16 q10, q10 // 4 32-bit elements for sum2
|
||||
vpadd.u32 d16,d16,d17 // 2 32-bit elements for adler
|
||||
vpadd.u32 d17,d20,d21 // 2 32-bit elements for sum2
|
||||
#else
|
||||
vadd.u16 d16, d17
|
||||
vadd.u16 d20, d21
|
||||
vpaddl.u16 d16, d16
|
||||
vpaddl.u16 d17, d20
|
||||
#endif
|
||||
subs len, #16 // decrement len by 16
|
||||
vadd.u32 adlersum2,adler16 // sum2 += old adler;
|
||||
vpadd.u32 d4,d17,d16 // s8 : 32-bit elements for sum2, s9 : 32-bit element for adler
|
||||
vadd.u32 adlersum2,d4 // update adler/sum2 with the new 16 bytes input
|
||||
|
||||
L_len_lessthan_16:
|
||||
adds len, #16 // post-increment len by 16
|
||||
beq L_len_is_zero_internal // if len==0, branch to len_is_zero_internal
|
||||
|
||||
// restore adler/sum2 into general registers for remaining (<16) bytes
|
||||
|
||||
vmov sum2, adler, adlersum2
|
||||
L_remaining_len_loop:
|
||||
ldrb t, [buf], #1 // *buf++;
|
||||
subs len, #1 // len--;
|
||||
add adler,t // adler += *buf
|
||||
add sum2,adler // sum2 += adler
|
||||
bgt L_remaining_len_loop // break if len<=0
|
||||
|
||||
vmov adlersum2, sum2, adler // move to double register for modulo operation
|
||||
|
||||
L_len_is_zero_internal:
|
||||
|
||||
// mod(alder,BASE); mod(sum2,BASE);
|
||||
|
||||
vmull.u32 q2,adlersum2,d3[1] // alder/BASE, sum2/BASE in Q47
|
||||
vshr.u64 q2,q2,#47 // take the integer part
|
||||
vpadd.u32 d4,d4,d5 // merge into a double word in d4
|
||||
vmls.u32 adlersum2,d4,d3[0] // (adler,sum2) -= floor[(adler,sum2)/BASE]*BASE
|
||||
|
||||
L_len_is_zero:
|
||||
|
||||
vmov sum2, adler, adlersum2 // restore adler/sum2 from (s12=sum2, s13=adler)
|
||||
add r0, adler, sum2, lsl #16 // to return adler | (sum2 << 16);
|
||||
ldmfd sp!, {r4, r5, pc} // restore registers and return
|
||||
|
||||
|
||||
// constants to be loaded into q registers
|
||||
.align 4 // 16 byte aligned
|
||||
|
||||
vec_table:
|
||||
|
||||
// coefficients for computing sum2
|
||||
.long 0x0d0e0f10 // s0
|
||||
.long 0x090a0b0c // s1
|
||||
.long 0x05060708 // s2
|
||||
.long 0x01020304 // s3
|
||||
|
||||
// coefficients for computing adler
|
||||
.long 0x01010101 // s4/d2
|
||||
.long 0x01010101 // s5
|
||||
|
||||
.long BASE // s6 : BASE
|
||||
.long 0x80078071 // s7 : 1/BASE in Q47
|
||||
|
||||
// q15 : d30.d31
|
||||
.long 0x1d1e1f20 // s0
|
||||
.long 0x191a1b1c // s1
|
||||
.long 0x15161718 // s2
|
||||
.long 0x11121314 // s3
|
||||
|
||||
NMAX_loc:
|
||||
.long NMAX // NMAX
|
||||
|
||||
#endif // defined(__arm__)
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple 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 Inc.
|
||||
*/
|
||||
|
||||
#if defined(__arm64__)
|
||||
|
||||
#define BASE 65521 /* largest prime smaller than 65536 */
|
||||
#define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
|
||||
|
||||
// Note: buf should have been 16-byte aligned in the caller function,
|
||||
|
||||
// uLong adler32_vec(unsigned int adler, unsigned int sum2, const Bytef* buf, int len) {
|
||||
// unsigned n;
|
||||
// while (len >= NMAX) {
|
||||
// len -= NMAX;
|
||||
// n = NMAX / 16; /* NMAX is divisible by 16 */
|
||||
// do {
|
||||
// DO16(buf); /* 16 sums unrolled */
|
||||
// buf += 16;
|
||||
// } while (--n);
|
||||
// MOD(adler);
|
||||
// MOD(sum2);
|
||||
// }
|
||||
// if (len) { /* avoid modulos if none remaining */
|
||||
// while (len >= 16) {
|
||||
// len -= 16;
|
||||
// DO16(buf);
|
||||
// buf += 16;
|
||||
// }
|
||||
// while (len--) {
|
||||
// adler += *buf++;
|
||||
// sum2 += adler;
|
||||
// }
|
||||
// MOD(adler);
|
||||
// MOD(sum2);
|
||||
// }
|
||||
// return adler | (sum2 << 16); /* return recombined sums */
|
||||
// }
|
||||
|
||||
|
||||
/*
|
||||
DO16 vectorization:
|
||||
given initial unsigned int sum2 and adler, and a new set of 16 input bytes (x[0:15]), it can be shown that
|
||||
sum2 += (16*adler + 16*x[0] + 15*x[1] + ... + 1*x[15]);
|
||||
adler += (x[0] + x[1] + ... + x[15]);
|
||||
|
||||
similarly, 2 DO16 = DO32 vectorization:
|
||||
given initial unsigned int sum2 and adler, and a new set of 32 input bytes (x[0:31]), it can be shown that
|
||||
sum2 += (32*adler + 32*x[0] + 31*x[1] + ... + 1*x[31]);
|
||||
adler += (x[0] + x[1] + ... + x[31]);
|
||||
|
||||
NMAX = 5552
|
||||
NMAX/16 = 347 = 173*2 + 1
|
||||
|
||||
therefore, for a block of 5552 bytes
|
||||
|
||||
n = 173;
|
||||
do {
|
||||
DO32(buf); buf+=32;
|
||||
} while (n--);
|
||||
|
||||
DO16(buf); buf+=16;
|
||||
MOD(adler);
|
||||
MOD(sum2);
|
||||
|
||||
for a residual remaining len bytes,
|
||||
while (len >= 32) {
|
||||
DO32(buf); buf += 32; len -= 32;
|
||||
}
|
||||
if (len>=16) {
|
||||
DO16(buf); buf += 16; len -= 16;
|
||||
}
|
||||
while (len--) {
|
||||
adler += *buf++;
|
||||
sum2 += adler;
|
||||
}
|
||||
MOD(adler);
|
||||
MOD(sum2);
|
||||
|
||||
|
||||
|
||||
|
||||
DO32:
|
||||
pack sum2:adler in a 64-bit register
|
||||
|
||||
0. sum2 += adler*32;
|
||||
|
||||
sum2:adler += (sum2:adler) << (32+5);
|
||||
|
||||
1. (32, 31, ..., 1) * ( x0, x1, ..., x31) (v2, v3) * (v0, v1)
|
||||
umull.8h v4, v2, v0
|
||||
umull2.8h v5, v2, v0
|
||||
umull.8h v6, v3, v1
|
||||
umull2.8h v7, v3, v1
|
||||
add.8h v4, v4, v5
|
||||
add.8h v6, v6, v7
|
||||
add.8h v4, v4, v6
|
||||
uaddlv s4, v4.8h
|
||||
|
||||
add sum2, sum2, s4
|
||||
|
||||
2. adler += (x0 + x1 + x2 + ... + x31)
|
||||
|
||||
uaddlp v0.8h, v0.16b
|
||||
uaddlp v1.8h, v1.16b
|
||||
uaddlv s0, v0.8h
|
||||
uaddlv s1, v1.8h
|
||||
add s0, s0, s1
|
||||
|
||||
add adler, adler, s0
|
||||
|
||||
|
||||
therefore, this is what can be done to vectorize the above computation
|
||||
1. 16-byte aligned vector load into q2 (x[0:x15])
|
||||
2. sum2 += (adler<<32); // pack adler/sum2 into a 64-bit word
|
||||
3. vmull.u8 (q9,q8),q2,d2 where d2 = (1,1,1,1...,1), (q9,q8) + 16 16-bit elements x[0:15]
|
||||
4. vmull.u8 (q11,q10),q2,q0 where q0 = (1,2,3,4...,16), (q11,q10) + 16 16-bit elements (16:1)*x[0:15]
|
||||
5. parallel add (with once expansion to 32-bit) (q9,q8) and (q11,q10) all the way to accumulate to adler and sum2
|
||||
|
||||
In this revision, whenever possible, 2 DO16 loops are combined into a DO32 loop.
|
||||
1. 32-byte aligned vector load into q2,q14 (x[0:x31])
|
||||
2. sum2 += (adler<<32);
|
||||
3. vmull.u8 (4 q registers),(q2,q14),d2 where d2 = (1,1,1,1...,1), (4 q registers) : 32 16-bit elements x[0:31]
|
||||
4. vmull.u8 (4 q registers),(q2,q14),(q0,q15) where q0 = (1,...,32), (4 q regs) : 32 16-bit elements (32:1)*x[0:31]
|
||||
5. parallel add (with once expansion to 32-bit) the pair of (4 q regs) all the way to accumulate to adler and sum2
|
||||
|
||||
This change improves the performance by ~ 0.55 cycle/uncompress byte on ARM Cortex-A8.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
MOD implementation:
|
||||
adler%BASE = adler - floor(adler*(1/BASE))*BASE; where (1/BASE) = 0x80078071 in Q47
|
||||
1. vmull.u32 q2,(adler,sum2),(1/BASE) // *(1/BASE) in Q47
|
||||
2. vshr.u64 q2,q2,#47 // floor function
|
||||
3. vpadd.u32 d4,d4,d5 // merge into a double word in d4
|
||||
4. vmls.u32 (adler,sum2),d4,d3[0] // (adler,sum2) -= floor[(adler,sum2)/BASE]*BASE
|
||||
|
||||
*/
|
||||
|
||||
|
||||
.text
|
||||
.align 2
|
||||
.globl _adler32_vec
|
||||
_adler32_vec:
|
||||
|
||||
|
||||
|
||||
#define adler w0
|
||||
#define sum2 w1
|
||||
#define buf x2
|
||||
#define len w3
|
||||
#define nmax w4
|
||||
#define nvec w6 // vecs = NMAX/16
|
||||
|
||||
#define t x7
|
||||
|
||||
#define vadlersum2 v5 // only lower part
|
||||
#define adlersum2 d5 // only lower part
|
||||
|
||||
add x0, x0, x1, lsl #32
|
||||
adrp t, vec_table@PAGE
|
||||
mov nmax, #NMAX // NMAX
|
||||
add t, t, vec_table@PAGEOFF
|
||||
ins vadlersum2.d[0], x0
|
||||
|
||||
|
||||
#if KERNEL
|
||||
sub x6, sp, #8*16
|
||||
sub sp, sp, #8*16
|
||||
st1.4s {v0,v1,v2,v3},[x6],#4*16
|
||||
st1.4s {v4,v5,v6,v7},[x6],#4*16
|
||||
#endif
|
||||
|
||||
cmp len, nmax // len vs NMAX
|
||||
ld1.4s {v0, v1}, [t], #2*16 // loading up coefficients for adler/sum2 computation
|
||||
ldr d7, [t] // for MOD operation
|
||||
b.lt L_len_lessthan_NMAX // if (len < NMAX) skip the while loop
|
||||
|
||||
sub len, len, nmax // pre-decrement len by NMAX
|
||||
|
||||
L_while_len_ge_NMAX_loop: // while (len>=NMAX) {
|
||||
|
||||
|
||||
// 5552/16 = 173*2 + 1, need 1 DO16 + 173 DO32
|
||||
|
||||
.macro DO16
|
||||
ld1.4s {v2},[buf] // 16 bytes input
|
||||
shl d6, adlersum2, #(4+32) // adler * 16
|
||||
umull.8h v4, v2, v1 // 16*x0, 15*x1, ..., 9*x7
|
||||
add adlersum2, adlersum2, d6 // sum2 += adler * 16
|
||||
umlal2.8h v4, v2, v1 // 8*x8, 7*x9, ..., 1*x15
|
||||
uaddlv h2, v2.16b // x0+x1+x2+...+x15
|
||||
uaddlv s4, v4.8h // 16*x0 + 15*x1 + ... + 1*x15
|
||||
zip1.2s v4, v2, v4
|
||||
add buf, buf, #16
|
||||
add.2s vadlersum2, vadlersum2, v4
|
||||
.endm
|
||||
|
||||
.macro DO32
|
||||
ld1.4s {v2,v3},[buf] // 32 bytes input
|
||||
shl d6, adlersum2, #(5+32) // adler * 32
|
||||
umull.8h v4, v2, v0 // 32*x0, 31*x1, ..., 25*x7
|
||||
add adlersum2, adlersum2, d6 // sum2 += adler * 32
|
||||
umlal2.8h v4, v2, v0 // accumulate 24*x8, 23*x9, ..., 17*x15
|
||||
uaddlv h2, v2.16b // x0+x1+x2+...+x15 and extend from byte to short
|
||||
umlal.8h v4, v3, v1 // accumulate 16*x16, 15*x17, ..., 9*x23
|
||||
uaddlv h6, v3.16b // x16+x17+x18+...+x31 and extend from byte to short
|
||||
umlal2.8h v4, v3, v1 // accumulate 8*x24, 7*x25, ..., 1*x31
|
||||
uaddl.4s v2, v2, v6 // x0+x1+...+x31 and extend from short to int
|
||||
uaddlv s4, v4.8h // 32*x0 + 31*x1 + ... + 1*x31
|
||||
zip1.2s v4, v2, v4
|
||||
add buf, buf, #32
|
||||
add.2s vadlersum2, vadlersum2, v4
|
||||
.endm
|
||||
|
||||
.macro MOD_BASE
|
||||
umull.2d v2,vadlersum2,v7[1]
|
||||
ushr.2d v2, v2, #47
|
||||
ins v2.s[1], v2.s[2]
|
||||
mls.2s vadlersum2,v2,v7[0]
|
||||
.endm
|
||||
|
||||
DO16
|
||||
mov nvec, #173 // number of DO32 loops
|
||||
|
||||
0:
|
||||
DO32
|
||||
|
||||
subs nvec, nvec, #1
|
||||
b.gt 0b
|
||||
|
||||
MOD_BASE // MOD(adler,sum2);
|
||||
|
||||
subs len, len, nmax // pre-decrement len by NMAX
|
||||
b.ge L_while_len_ge_NMAX_loop // repeat while len >= NMAX
|
||||
|
||||
adds len, len, nmax // post-increment len by NMAX
|
||||
|
||||
L_len_lessthan_NMAX:
|
||||
|
||||
subs len, len, #32 // pre-decrement len by 32
|
||||
b.lt L_len_lessthan_32 // if len < 32, branch to len16_loop
|
||||
|
||||
L_len32_loop:
|
||||
|
||||
DO32
|
||||
|
||||
subs len, len, #32 // len -= 32;
|
||||
b.ge L_len32_loop
|
||||
|
||||
L_len_lessthan_32:
|
||||
|
||||
tst len, #16
|
||||
b.eq 1f
|
||||
|
||||
DO16
|
||||
|
||||
1:
|
||||
ands len, len, #15
|
||||
b.eq L_len_is_zero
|
||||
|
||||
|
||||
L_remaining_len_loop:
|
||||
ldrb w0, [buf], #1 // *buf++;
|
||||
sub len, len, #1
|
||||
ins v2.d[0], x0
|
||||
|
||||
add adlersum2, adlersum2, d2 // adler += *buf
|
||||
shl d6, adlersum2, #32 // shift adler up to sum2 position
|
||||
add adlersum2, adlersum2, d6 // sum2 += adler
|
||||
cbnz len, L_remaining_len_loop // break if len<=0
|
||||
|
||||
|
||||
L_len_is_zero:
|
||||
|
||||
|
||||
MOD_BASE // mod(alder,BASE); mod(sum2,BASE);
|
||||
|
||||
umov w0, vadlersum2.s[0] // adler
|
||||
umov w1, vadlersum2.s[1] // sum2
|
||||
add w0, w0, w1, lsl #16 // to return adler | (sum2 << 16);
|
||||
|
||||
#if KERNEL
|
||||
ld1.4s {v0,v1,v2,v3},[sp],#4*16
|
||||
ld1.4s {v4,v5,v6,v7},[sp],#4*16
|
||||
#endif
|
||||
|
||||
ret lr
|
||||
|
||||
// constants to be loaded into q registers
|
||||
.align 4 // 16 byte aligned
|
||||
|
||||
vec_table:
|
||||
|
||||
// coefficients for computing sum2
|
||||
.long 0x1d1e1f20 // s0
|
||||
.long 0x191a1b1c // s1
|
||||
.long 0x15161718 // s2
|
||||
.long 0x11121314 // s3
|
||||
.long 0x0d0e0f10 // s0
|
||||
.long 0x090a0b0c // s1
|
||||
.long 0x05060708 // s2
|
||||
.long 0x01020304 // s3
|
||||
|
||||
.long BASE // s6 : BASE
|
||||
.long 0x80078071 // s7 : 1/BASE in Q47
|
||||
|
||||
|
||||
|
||||
#endif // __arm64__
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/* $NetBSD: crc.c,v 1.18 2006/09/04 20:01:10 dsl Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* James W. Williams of NASA Goddard Space Flight Center.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <lib/cksum.h>
|
||||
|
||||
static const uint32_t crctab[] = {
|
||||
0x0,
|
||||
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
|
||||
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
|
||||
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
|
||||
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
|
||||
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
|
||||
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
|
||||
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
|
||||
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
|
||||
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
|
||||
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||||
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
|
||||
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
|
||||
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
|
||||
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
|
||||
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
|
||||
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
|
||||
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||||
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
|
||||
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
|
||||
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
|
||||
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
|
||||
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
|
||||
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
|
||||
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
|
||||
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
|
||||
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
|
||||
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
|
||||
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||||
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
|
||||
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
|
||||
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
|
||||
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
|
||||
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
|
||||
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
|
||||
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||||
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
|
||||
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
|
||||
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||
};
|
||||
|
||||
#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
|
||||
|
||||
/*
|
||||
* Compute a POSIX 1003.2 checksum. Length is included in the checksum.
|
||||
*/
|
||||
uint32_t
|
||||
crc(const void *buf, size_t len)
|
||||
{
|
||||
const uint8_t *p = buf;
|
||||
size_t nr = len;
|
||||
uint32_t thecrc = INITIAL_CRC;
|
||||
|
||||
for (p = buf; nr; p++, nr--)
|
||||
COMPUTE(thecrc, *p);
|
||||
|
||||
/* Include the length of the buffer. */
|
||||
for (; len != 0; len >>= 8) {
|
||||
COMPUTE(thecrc, len & 0xff);
|
||||
}
|
||||
return ~thecrc;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
crc_byte(uint32_t thecrc, unsigned int byte_val)
|
||||
{
|
||||
COMPUTE(thecrc, byte_val & 0xff);
|
||||
return thecrc;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <lib/cksum.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Table of CRCs of all 8-bit messages. */
|
||||
static uint32_t crc32_table[256];
|
||||
|
||||
/* Flag: has the table been computed? Initially false. */
|
||||
static int crc32_table_computed = 0;
|
||||
|
||||
/* Make the table for a fast CRC. */
|
||||
static void make_crc32_table(void)
|
||||
{
|
||||
uint32_t c;
|
||||
int n, k;
|
||||
|
||||
for (n = 0; n < 256; n++) {
|
||||
c = (uint32_t) n;
|
||||
for (k = 0; k < 8; k++) {
|
||||
if (c & 1)
|
||||
c = 0xedb88320L ^ (c >> 1);
|
||||
else
|
||||
c = c >> 1;
|
||||
}
|
||||
crc32_table[n] = c;
|
||||
}
|
||||
crc32_table_computed = 1;
|
||||
}
|
||||
|
||||
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
|
||||
should be initialized to all 1's, and the transmitted value
|
||||
is the 1's complement of the final running CRC (see the
|
||||
crc32() routine below). */
|
||||
|
||||
uint32_t update_crc32(uint32_t crc, const uint8_t *buf,
|
||||
int len)
|
||||
{
|
||||
uint32_t c = crc;
|
||||
int n;
|
||||
|
||||
if (!crc32_table_computed)
|
||||
make_crc32_table();
|
||||
for (n = 0; n < len; n++) {
|
||||
c = crc32_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Return the CRC of the bytes buf[0..len-1]. */
|
||||
uint32_t crc32(const uint8_t *buf, int len)
|
||||
{
|
||||
return update_crc32(0xffffffffL, buf, len) ^ 0xffffffffL;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009, 2013-2014 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <lib/cksum.h>
|
||||
#include <sys/menu.h>
|
||||
#include <drivers/sha1.h>
|
||||
|
||||
int do_crc(int argc, struct cmd_arg *args)
|
||||
{
|
||||
if (argc < 3) {
|
||||
printf("not enough arguments.\n");
|
||||
printf("%s <address> <len>\n", args[0].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!security_allow_memory((void *)args[1].u, args[2].u)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t crc32 = crc((void *)args[1].u, args[2].u);
|
||||
printf("crc %u (length %zu)\n", crc32, args[2].u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LIBCKSUM_DIR := $(GET_LOCAL_DIR)
|
||||
LIBCKSUM_BUILD := $(call TOLIBDIR,$(LIBCKSUM_DIR)/LIBCKSUM.a)
|
||||
COMMONLIBS += LIBCKSUM
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
LIBCKSUM_OBJS := \
|
||||
$(LIBCKSUM_DIR)/adler32.o \
|
||||
$(LIBCKSUM_DIR)/$(ARCH)/adler32vec.o \
|
||||
$(LIBCKSUM_DIR)/crc.o \
|
||||
$(LIBCKSUM_DIR)/crc32.o \
|
||||
$(LIBCKSUM_DIR)/debug.o \
|
||||
$(LIBCKSUM_DIR)/siphash.o
|
||||
|
||||
LIBCKSUM_OBJS := $(call TOLIBOBJDIR,$(LIBCKSUM_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBCKSUM_OBJS:%o=%d)
|
||||
|
||||
$(LIBCKSUM_BUILD): $(LIBCKSUM_OBJS)
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2007-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 Inc.
|
||||
#
|
||||
|
||||
OPTIONS += WITH_CKSUM=1
|
||||
LIBRARY_MODULES += lib/cksum
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
SipHash reference C implementation
|
||||
|
||||
Copyright (c) 2012 Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
|
||||
Copyright (c) 2012 Daniel J. Bernstein <djb@cr.yp.to>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along with
|
||||
this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
typedef uint64_t u64;
|
||||
typedef uint32_t u32;
|
||||
typedef uint8_t u8;
|
||||
|
||||
#define cROUNDS 2
|
||||
#define dROUNDS 4
|
||||
|
||||
#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
|
||||
|
||||
#define U32TO8_LE(p, v) \
|
||||
(p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
|
||||
(p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
|
||||
|
||||
#define U64TO8_LE(p, v) \
|
||||
U32TO8_LE((p), (u32)((v) )); \
|
||||
U32TO8_LE((p) + 4, (u32)((v) >> 32));
|
||||
|
||||
#define U8TO64_LE(p) \
|
||||
(((u64)((p)[0]) ) | \
|
||||
((u64)((p)[1]) << 8) | \
|
||||
((u64)((p)[2]) << 16) | \
|
||||
((u64)((p)[3]) << 24) | \
|
||||
((u64)((p)[4]) << 32) | \
|
||||
((u64)((p)[5]) << 40) | \
|
||||
((u64)((p)[6]) << 48) | \
|
||||
((u64)((p)[7]) << 56))
|
||||
|
||||
#define SIPROUND \
|
||||
do { \
|
||||
v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
|
||||
v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
|
||||
v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
|
||||
v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
|
||||
} while(0)
|
||||
|
||||
/* SipHash-2-4 */
|
||||
void siphash( uint8_t *out, const uint8_t *in, size_t inlen, const uint8_t *k )
|
||||
{
|
||||
/* "somepseudorandomlygeneratedbytes" */
|
||||
u64 v0 = 0x736f6d6570736575ULL;
|
||||
u64 v1 = 0x646f72616e646f6dULL;
|
||||
u64 v2 = 0x6c7967656e657261ULL;
|
||||
u64 v3 = 0x7465646279746573ULL;
|
||||
u64 b;
|
||||
u64 k0 = U8TO64_LE( k );
|
||||
u64 k1 = U8TO64_LE( k + 8 );
|
||||
u64 m;
|
||||
int i;
|
||||
const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
|
||||
const int left = inlen & 7;
|
||||
b = ( ( u64 )inlen ) << 56;
|
||||
v3 ^= k1;
|
||||
v2 ^= k0;
|
||||
v1 ^= k1;
|
||||
v0 ^= k0;
|
||||
|
||||
for ( ; in != end; in += 8 )
|
||||
{
|
||||
m = U8TO64_LE( in );
|
||||
#ifdef SIPHASH_DEBUG
|
||||
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
|
||||
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
|
||||
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
|
||||
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
|
||||
printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
|
||||
#endif
|
||||
v3 ^= m;
|
||||
|
||||
for( i=0; i<cROUNDS; ++i ) SIPROUND;
|
||||
|
||||
v0 ^= m;
|
||||
}
|
||||
|
||||
switch( left )
|
||||
{
|
||||
case 7: b |= ( ( u64 )in[ 6] ) << 48;
|
||||
|
||||
case 6: b |= ( ( u64 )in[ 5] ) << 40;
|
||||
|
||||
case 5: b |= ( ( u64 )in[ 4] ) << 32;
|
||||
|
||||
case 4: b |= ( ( u64 )in[ 3] ) << 24;
|
||||
|
||||
case 3: b |= ( ( u64 )in[ 2] ) << 16;
|
||||
|
||||
case 2: b |= ( ( u64 )in[ 1] ) << 8;
|
||||
|
||||
case 1: b |= ( ( u64 )in[ 0] ); break;
|
||||
|
||||
case 0: break;
|
||||
}
|
||||
|
||||
#ifdef SIPHASH_DEBUG
|
||||
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
|
||||
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
|
||||
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
|
||||
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
|
||||
printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
|
||||
#endif
|
||||
v3 ^= b;
|
||||
|
||||
for( i=0; i<cROUNDS; ++i ) SIPROUND;
|
||||
|
||||
v0 ^= b;
|
||||
#ifdef SIPHASH_DEBUG
|
||||
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
|
||||
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
|
||||
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
|
||||
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
|
||||
#endif
|
||||
v2 ^= 0xff;
|
||||
|
||||
for( i=0; i<dROUNDS; ++i ) SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
U64TO8_LE( out, b );
|
||||
}
|
||||
|
||||
|
||||
void siphash_aligned(uint64_t *out, const uint64_t *in_aligned, size_t inlen, const uint64_t *k)
|
||||
{
|
||||
/* "somepseudorandomlygeneratedbytes" */
|
||||
u64 v0 = 0x736f6d6570736575ULL;
|
||||
u64 v1 = 0x646f72616e646f6dULL;
|
||||
u64 v2 = 0x6c7967656e657261ULL;
|
||||
u64 v3 = 0x7465646279746573ULL;
|
||||
u64 b;
|
||||
u64 k0 = k[0];
|
||||
u64 k1 = k[1];
|
||||
u64 m;
|
||||
int i;
|
||||
const u8 *in = (const u8 *)in_aligned;
|
||||
const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
|
||||
const int left = inlen & 7;
|
||||
b = ( ( u64 )inlen ) << 56;
|
||||
v3 ^= k1;
|
||||
v2 ^= k0;
|
||||
v1 ^= k1;
|
||||
v0 ^= k0;
|
||||
|
||||
ASSERT(((uintptr_t)in_aligned & (sizeof(u64) - 1)) == 0);
|
||||
|
||||
for ( ; in != end; in += 8 )
|
||||
{
|
||||
m = *(u64 *)in;
|
||||
#ifdef SIPHASH_DEBUG
|
||||
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
|
||||
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
|
||||
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
|
||||
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
|
||||
printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
|
||||
#endif
|
||||
v3 ^= m;
|
||||
|
||||
for( i=0; i<cROUNDS; ++i ) SIPROUND;
|
||||
|
||||
v0 ^= m;
|
||||
}
|
||||
|
||||
switch( left )
|
||||
{
|
||||
case 7: b |= ( ( u64 )in[ 6] ) << 48;
|
||||
|
||||
case 6: b |= ( ( u64 )in[ 5] ) << 40;
|
||||
|
||||
case 5: b |= ( ( u64 )in[ 4] ) << 32;
|
||||
|
||||
case 4: b |= ( ( u64 )in[ 3] ) << 24;
|
||||
|
||||
case 3: b |= ( ( u64 )in[ 2] ) << 16;
|
||||
|
||||
case 2: b |= ( ( u64 )in[ 1] ) << 8;
|
||||
|
||||
case 1: b |= ( ( u64 )in[ 0] ); break;
|
||||
|
||||
case 0: break;
|
||||
}
|
||||
|
||||
#ifdef SIPHASH_DEBUG
|
||||
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
|
||||
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
|
||||
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
|
||||
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
|
||||
printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
|
||||
#endif
|
||||
v3 ^= b;
|
||||
|
||||
for( i=0; i<cROUNDS; ++i ) SIPROUND;
|
||||
|
||||
v0 ^= b;
|
||||
#ifdef SIPHASH_DEBUG
|
||||
printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
|
||||
printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
|
||||
printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
|
||||
printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
|
||||
#endif
|
||||
v2 ^= 0xff;
|
||||
|
||||
for( i=0; i<dROUNDS; ++i ) SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
*out = b;
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
#include <unittest.h>
|
||||
#include <lib/cksum.h>
|
||||
|
||||
struct crc_test {
|
||||
const char *data;
|
||||
uint32_t len;
|
||||
uint32_t expected;
|
||||
};
|
||||
|
||||
static struct crc_test crc32_tests[] = {
|
||||
{ "", 0, 0x00000000 },
|
||||
{ "a", 1, 0xe8b7be43 },
|
||||
{ "abc", 3, 0x352441c2 },
|
||||
{ "aaaa", 4, 0xad98e545 },
|
||||
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x4c2750bd }
|
||||
};
|
||||
|
||||
void test_crc32(uintptr_t param)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof crc32_tests / sizeof(crc32_tests[0]); i++) {
|
||||
struct crc_test *test = &crc32_tests[i];
|
||||
uint32_t calculated = crc32((const uint8_t *)test->data, test->len);
|
||||
|
||||
TEST_ASSERT_EQ(calculated, test->expected);
|
||||
}
|
||||
}
|
||||
|
||||
void test_update_crc32(uintptr_t param)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof crc32_tests / sizeof(crc32_tests[0]); i++) {
|
||||
struct crc_test *test = &crc32_tests[i];
|
||||
|
||||
uint32_t calculated = 0xffffffff;
|
||||
const uint8_t *data = (const uint8_t *)test->data;
|
||||
|
||||
if (test->len != 0) {
|
||||
calculated = update_crc32(calculated, data, 1);
|
||||
calculated = update_crc32(calculated, data + 1, test->len - 1);
|
||||
} else {
|
||||
calculated = update_crc32(calculated, data, test->len);
|
||||
}
|
||||
|
||||
calculated ^= 0xffffffff;
|
||||
|
||||
TEST_ASSERT_EQ(calculated, test->expected);
|
||||
|
||||
// updating with data of length 0 shouldn't have an effect
|
||||
calculated = update_crc32(calculated, (const uint8_t *)"a", 0);
|
||||
|
||||
TEST_ASSERT_EQ(calculated, test->expected);
|
||||
}
|
||||
}
|
||||
|
||||
// generated with the "cksum" commandline tool
|
||||
static struct crc_test crc_tests[] = {
|
||||
{ "", 0, 4294967295 },
|
||||
{ "a", 1, 1220704766 },
|
||||
{ "abc", 3, 1219131554 },
|
||||
{ "aaaa", 4, 3100911197 },
|
||||
{ "abcdefghijklmnopqrstuvwxyz", 26, 2713270184 },
|
||||
{ "\0\0\0\0", 4, 3975907619 },
|
||||
};
|
||||
|
||||
void test_crc(uintptr_t data)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof crc_tests / sizeof(crc_tests[0]); i++) {
|
||||
struct crc_test *test = &crc_tests[i];
|
||||
uint32_t calculated = crc((const uint8_t *)test->data, test->len);
|
||||
|
||||
TEST_ASSERT_EQ(calculated, test->expected);
|
||||
}
|
||||
}
|
||||
|
||||
void test_crc_byte(uintptr_t data)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof crc_tests / sizeof(crc_tests[0]); i++) {
|
||||
struct crc_test *test = &crc_tests[i];
|
||||
uint32_t calculated = INITIAL_CRC;
|
||||
|
||||
for (uint32_t j = 0; j < test->len; j++)
|
||||
calculated = crc_byte(calculated, ((const uint8_t *)test->data)[j]);
|
||||
|
||||
for (uint32_t n = test->len; n != 0; n >>= 8)
|
||||
calculated = crc_byte(calculated, n & 0xff);
|
||||
|
||||
calculated = ~calculated;
|
||||
|
||||
TEST_ASSERT_EQ(calculated, test->expected);
|
||||
}
|
||||
}
|
||||
|
||||
static struct crc_test adler32_tests[] = {
|
||||
{ "", 0, 0x00000001 },
|
||||
{ "a", 1, 0x00620062 },
|
||||
{ "abc", 3, 0x024d0127 },
|
||||
{ "message digest", 14, 0x29750586 },
|
||||
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x90860b20 },
|
||||
};
|
||||
|
||||
void test_adler32(uintptr_t data)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof adler32_tests / sizeof(adler32_tests[0]); i++) {
|
||||
struct crc_test *test = &adler32_tests[i];
|
||||
uint32_t calculated = adler32((const uint8_t *)test->data, test->len);
|
||||
|
||||
TEST_ASSERT_EQ(calculated, test->expected);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t siphash_vectors[64][SIPHASH_HASH_SIZE] =
|
||||
{
|
||||
{ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
|
||||
{ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
|
||||
{ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
|
||||
{ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
|
||||
{ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
|
||||
{ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
|
||||
{ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
|
||||
{ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
|
||||
{ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
|
||||
{ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
|
||||
{ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
|
||||
{ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
|
||||
{ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
|
||||
{ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
|
||||
{ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
|
||||
{ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
|
||||
{ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
|
||||
{ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
|
||||
{ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
|
||||
{ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
|
||||
{ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
|
||||
{ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
|
||||
{ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
|
||||
{ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
|
||||
{ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
|
||||
{ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
|
||||
{ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
|
||||
{ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
|
||||
{ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
|
||||
{ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
|
||||
{ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
|
||||
{ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
|
||||
{ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
|
||||
{ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
|
||||
{ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
|
||||
{ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
|
||||
{ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
|
||||
{ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
|
||||
{ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
|
||||
{ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
|
||||
{ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
|
||||
{ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
|
||||
{ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
|
||||
{ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
|
||||
{ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
|
||||
{ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
|
||||
{ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
|
||||
{ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
|
||||
{ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
|
||||
{ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
|
||||
{ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
|
||||
{ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
|
||||
{ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
|
||||
{ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
|
||||
{ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
|
||||
{ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
|
||||
{ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
|
||||
{ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
|
||||
{ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
|
||||
{ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
|
||||
{ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
|
||||
{ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
|
||||
{ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
|
||||
{ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }
|
||||
};
|
||||
|
||||
void test_siphash(uintptr_t context)
|
||||
{
|
||||
uint8_t in[64], out[SIPHASH_HASH_SIZE], k[SIPHASH_KEY_SIZE];
|
||||
unsigned i;
|
||||
|
||||
for(i = 0; i < 16; ++i)
|
||||
k[i] = i;
|
||||
|
||||
for(i = 0; i < sizeof(in); ++i)
|
||||
{
|
||||
in[i] = i;
|
||||
siphash(out, in, i, k);
|
||||
|
||||
TEST_ASSERT_MEM_EQ(out, siphash_vectors[i], sizeof(out));
|
||||
}
|
||||
}
|
||||
|
||||
void test_siphash_aligned(uintptr_t context)
|
||||
{
|
||||
uint64_t in_aligned[64 / 8];
|
||||
uint64_t out[SIPHASH_HASH_SIZE / 8];
|
||||
uint64_t key_aligned[SIPHASH_KEY_SIZE / 8];
|
||||
uint8_t *in = (uint8_t *)&in_aligned[0];
|
||||
uint8_t *key = (uint8_t *)&key_aligned[0];
|
||||
unsigned i;
|
||||
|
||||
for(i = 0; i < 16; ++i)
|
||||
key[i] = i;
|
||||
|
||||
for(i = 0; i < sizeof(in); ++i)
|
||||
{
|
||||
in[i] = i;
|
||||
siphash_aligned(out, in_aligned, i, key_aligned);
|
||||
|
||||
TEST_ASSERT_MEM_EQ(out, siphash_vectors[i], sizeof(out));
|
||||
}
|
||||
}
|
||||
|
||||
struct test_suite cksum_suite = {
|
||||
.name = "cksum",
|
||||
.test_cases = {
|
||||
{ "crc32", test_crc32, 0, "Tests the crc32 checksum function" },
|
||||
{ "update_crc32", test_update_crc32, 0, "Tests the update_crc32 checksum function" },
|
||||
{ "crc", test_crc, 0, "Tests the crc POSIX 1003.2 checksum function" },
|
||||
{ "crc_byte", test_crc_byte, 0, "Tests the crc_byte bytewise POSIX 1003.2 checksum function" },
|
||||
{ "adler32", test_adler32, 0, "Tests the adler32 checksum function" },
|
||||
{ "siphash", test_siphash, 0, "Tests the siphash checksum function" },
|
||||
{ "siphash_aligned", test_siphash_aligned, 0, "Tests the siphash_aligned checksum function" },
|
||||
TEST_CASE_LAST
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE(cksum_suite);
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := cksum
|
||||
|
||||
TEST_OBJS := \
|
||||
$(LOCAL_DIR)/adler32.o \
|
||||
$(LOCAL_DIR)/crc.o \
|
||||
$(LOCAL_DIR)/crc32.o \
|
||||
$(LOCAL_DIR)/siphash.o
|
||||
|
||||
TEST_SUPPORT_OBJS := \
|
||||
tests/unittest-main.o \
|
||||
$(LOCAL_DIR)/tests.o
|
||||
|
||||
ifeq ($(ARM_ARCH),armv7)
|
||||
TEST_OBJS += \
|
||||
$(LOCAL_DIR)/arm/adler32vec.o
|
||||
endif
|
||||
|
||||
ifeq ($(ARM_ARCH),armv7k)
|
||||
TEST_OBJS += \
|
||||
$(LOCAL_DIR)/arm/adler32vec.o
|
||||
endif
|
||||
|
||||
ifeq ($(ARM_ARCH),arm64)
|
||||
TEST_OBJS += \
|
||||
$(LOCAL_DIR)/arm64/adler32vec.o
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include <lib/syscfg.h>
|
||||
#include <lib/env.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "devicetree_private.h"
|
||||
|
||||
/*
|
||||
* Device Tree Parsing Library
|
||||
*
|
||||
* This library implements the device tree serializer/updater/deserializer.
|
||||
*
|
||||
* There should be more stuff here, but for now a quick note.
|
||||
*
|
||||
* The dt_size variable contains the size the device tree will have upon
|
||||
* serialization. Any modification to the deserialized device tree must
|
||||
* update this variable so that it continues to contain the size the device
|
||||
* tree will have on re-serialization. This is because other modules need
|
||||
* to know the size of the device tree in order to properly lay out memory
|
||||
* (for example, in order to allocate a region of the kernel's memory map)
|
||||
*/
|
||||
|
||||
static void free_node(dt_node_t *node);
|
||||
void dt_free(dt_node_t *dt);
|
||||
|
||||
static size_t dt_size;
|
||||
static dt_node_t *dt_root;
|
||||
static void *dt_data;
|
||||
static bool dt_sealed;
|
||||
|
||||
#define ROUND_SIZE(s) (((s) + 3) & ~3)
|
||||
|
||||
void dt_init(void)
|
||||
{
|
||||
dt_size = 0;
|
||||
if (dt_root != NULL) {
|
||||
dt_free(dt_root);
|
||||
dt_root = NULL;
|
||||
}
|
||||
dt_data = NULL;
|
||||
dt_sealed = false;
|
||||
}
|
||||
|
||||
size_t dt_get_size(void)
|
||||
{
|
||||
return dt_size;
|
||||
}
|
||||
|
||||
static void free_node(dt_node_t *node)
|
||||
{
|
||||
if (node->props != NULL) {
|
||||
for (uint32_t i = 0; i < node->nprops; i++) {
|
||||
if (node->props[i].name_malloced)
|
||||
free(node->props[i].name);
|
||||
if (node->props[i].data_malloced)
|
||||
free(node->props[i].data);
|
||||
}
|
||||
free(node->props);
|
||||
node->props = NULL;
|
||||
}
|
||||
if (node->children != NULL) {
|
||||
for (uint32_t i = 0; i < node->nchildren; i++) {
|
||||
free_node(&node->children[i]);
|
||||
}
|
||||
free(node->children);
|
||||
node->children = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Replaces placeholder values during deserialization. The placeholders are normal strings,
|
||||
* but the size field has bit 31 set to indicate it's a placeholder. If iBoot successfully
|
||||
* finds the requested data, it replaces the placeholder value with the requested data
|
||||
* (and sets bit 31 of the size back to 0). If iBoot doesn't find the requested data,
|
||||
* it removes the property from the device tree.
|
||||
*
|
||||
* Two kinds of placeholders are currently supported:
|
||||
* syscfg/<SKey> - looks up the key 'SKey' in syscfg
|
||||
* syscfg/<SKey>/<len> - looks up the key 'SKey' in syscfg, and pads/truncates to specified
|
||||
* length (for compatibility with old-style devicetree placeholders)
|
||||
* macaddr/<envname> - looks up the mac address envname in the environment using env_get_ethaddr
|
||||
* zeroes/<bytes> - creates a property with bytes zero bytes
|
||||
*
|
||||
* Multiple placeholders can be specified, with options separated by commas. The first
|
||||
* placeholder found is used.
|
||||
*/
|
||||
static bool expand_placeholder_prop(dtprop_t *prop)
|
||||
{
|
||||
uint32_t alloc_size;
|
||||
uint8_t ethaddr[6];
|
||||
uint8_t dummy = 0;
|
||||
void *data = NULL;
|
||||
uint32_t data_size = 0;
|
||||
uint32_t data_pad_size = 0;
|
||||
char *placeholder;
|
||||
char *next_placeholder;
|
||||
// make sure placeholder is null-terminated
|
||||
if (memchr(prop->data, '\0', prop->size) == NULL) {
|
||||
panic("devicetree placeholder property '%s' missing null terminator", prop->name);
|
||||
}
|
||||
|
||||
placeholder = prop->data;
|
||||
next_placeholder = placeholder;
|
||||
|
||||
while ((placeholder = strsep(&next_placeholder, ",")) != NULL) {
|
||||
if (memcmp(placeholder, "syscfg/", 7) == 0) {
|
||||
char *details = placeholder + 7;
|
||||
uint8_t *tag_str;
|
||||
char *len_str;
|
||||
uint32_t len;
|
||||
uint32_t tag;
|
||||
|
||||
tag_str = (uint8_t *)details;
|
||||
|
||||
len_str = details;
|
||||
strsep(&len_str, "/");
|
||||
|
||||
if (strlen(details) != 4) {
|
||||
panic("dt: wrong length on placeholder %s", placeholder);
|
||||
}
|
||||
|
||||
tag = tag_str[0] << 24 | tag_str[1] << 16 | tag_str[2] << 8 | tag_str[3];
|
||||
|
||||
if (syscfg_find_tag(tag, &data, &data_size)) {
|
||||
// For compatibility with old-style devicetree placeholders, a length
|
||||
// can be specified to truncate/pad the syscfg data to
|
||||
if (len_str != NULL) {
|
||||
len = (uint32_t)strtoul(len_str, NULL, 0);
|
||||
if (len > data_size) {
|
||||
data_pad_size = len - data_size;
|
||||
} else {
|
||||
data_size = len;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else if (memcmp(placeholder, "macaddr/", 8) == 0) {
|
||||
char *details = placeholder + 8;
|
||||
|
||||
if (env_get_ethaddr(details, ethaddr) == 0) {
|
||||
data = ethaddr;
|
||||
data_size = sizeof(ethaddr);
|
||||
break;
|
||||
}
|
||||
} else if (memcmp(placeholder, "zeroes/", 7) == 0) {
|
||||
char *details = placeholder + 7;
|
||||
|
||||
// Set data to a dummy pointer so that data != NULL but request
|
||||
// copying 0 bytes. The padding will all get zeroed, resulting
|
||||
// in a property value with the requested number of zeroes
|
||||
data = &dummy;
|
||||
data_size = 0;
|
||||
data_pad_size = (uint32_t)strtoul(details, NULL, 0);
|
||||
}
|
||||
|
||||
placeholder = next_placeholder;
|
||||
}
|
||||
|
||||
uint32_t original_size = ROUND_SIZE(prop->size);
|
||||
|
||||
if (data != NULL) {
|
||||
alloc_size = ROUND_SIZE(data_size + data_pad_size);
|
||||
|
||||
// Since this function is called during initial parsing, we know
|
||||
// that the storage for the placeholder was in the original devicetree
|
||||
// buffer and not malloced in a new buffer. We only need a new buffer
|
||||
// if the new data is longer than the placeholder
|
||||
if (alloc_size > original_size) {
|
||||
prop->data = malloc(alloc_size);
|
||||
prop->data_malloced = true;
|
||||
}
|
||||
memcpy(prop->data, data, data_size);
|
||||
// zero-fill any padding bytes
|
||||
memset((uint8_t *)prop->data + data_size, 0, alloc_size - data_size);
|
||||
|
||||
prop->size = data_size + data_pad_size;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_node(dt_node_t *node, uint8_t **addrptr, const uint8_t *endaddr)
|
||||
{
|
||||
uint8_t *addr = *addrptr;
|
||||
|
||||
if ((size_t)(endaddr - addr) < 2 * sizeof(uint32_t))
|
||||
goto error;
|
||||
|
||||
memcpy(&node->nprops, addr, 4);
|
||||
memcpy(&node->nchildren, addr + 4, 4);
|
||||
addr += 2 * sizeof(uint32_t);
|
||||
dt_size += 2 * sizeof(uint32_t);
|
||||
|
||||
// Sanity checks
|
||||
if (node->nprops > 1024) {
|
||||
dprintf(DEBUG_INFO, "dt: too many properties\n");
|
||||
goto error;
|
||||
}
|
||||
if (node->nchildren > 1024) {
|
||||
dprintf(DEBUG_INFO, "dt: too many children\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (node->nprops > 0)
|
||||
node->props = calloc(sizeof(*node->props), node->nprops);
|
||||
if (node->nchildren > 0)
|
||||
node->children = calloc(sizeof(*node->children), node->nchildren);
|
||||
|
||||
// Parse each property, recording its size, the address of the name,
|
||||
// and the address of its data.
|
||||
// We could find the "name" property and cache it here, but instead
|
||||
// we'll do that if we ever traverse this node looking for a node
|
||||
// by path.
|
||||
for(uint32_t i = 0; i < node->nprops; i++) {
|
||||
dtprop_t *prop;
|
||||
uint32_t size_and_flags;
|
||||
bool prop_removed = false;
|
||||
|
||||
ASSERT(addr <= endaddr);
|
||||
|
||||
prop = &node->props[i];
|
||||
|
||||
if((size_t)(endaddr - addr) < kPropNameLength + sizeof(uint32_t)) {
|
||||
dprintf(DEBUG_INFO, "dt: not enough bytes for property name '%s'\n", (char *)addr);
|
||||
goto error;
|
||||
}
|
||||
if (memchr(addr, 0, kPropNameLength) == NULL) {
|
||||
dprintf(DEBUG_INFO, "dt: unterminated property name '%s'\n", (char *)addr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
prop->name = (char *)addr;
|
||||
|
||||
memcpy(&size_and_flags, addr + kPropNameLength, sizeof(uint32_t));
|
||||
addr += kPropNameLength + sizeof(uint32_t);
|
||||
|
||||
prop->size = size_and_flags & DT_PROP_SIZE_MASK;
|
||||
|
||||
if ((size_t)(endaddr - addr) < ROUND_SIZE(prop->size)) {
|
||||
dprintf(DEBUG_INFO, "dt: not enough bytes for property '%s'\n", prop->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
prop->data = addr;
|
||||
addr += ROUND_SIZE(prop->size);
|
||||
|
||||
if (size_and_flags & DT_PROP_FLAG_PLACEHOLDER) {
|
||||
// if placeholder expansion fails, then recycle the property slot
|
||||
// we used for the placeholder
|
||||
prop_removed = !expand_placeholder_prop(prop);
|
||||
if (prop_removed) {
|
||||
node->nprops--;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
// Account for the size of the property after any placeholder expansion has occurred
|
||||
if (!prop_removed)
|
||||
dt_size += kPropNameLength + sizeof(uint32_t) + ROUND_SIZE(prop->size);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < node->nchildren; i++) {
|
||||
if (!parse_node(&node->children[i], &addr, endaddr))
|
||||
goto error;
|
||||
}
|
||||
|
||||
*addrptr = addr;
|
||||
|
||||
return true;
|
||||
error:
|
||||
free_node(node);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
dt_node_t *dt_deserialize(void *dtaddr, size_t dtlength)
|
||||
{
|
||||
uint8_t *startptr;
|
||||
uint8_t *endptr;
|
||||
dt_node_t *node;
|
||||
|
||||
dt_init();
|
||||
|
||||
node = calloc(sizeof(*node), 1);
|
||||
startptr = dtaddr;
|
||||
endptr = dtaddr + dtlength;
|
||||
|
||||
if (!parse_node(node, &startptr, endptr)) {
|
||||
dt_size = 0;
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dt_root = node;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void dt_free(dt_node_t *dt)
|
||||
{
|
||||
free_node(dt);
|
||||
free(dt);
|
||||
}
|
||||
|
||||
static void write_prop(char *name, void *data, uint32_t data_size, uint8_t **bufptr, uint8_t *end)
|
||||
{
|
||||
uint8_t *buf = *bufptr;
|
||||
uint32_t prop_buf_size = ROUND_SIZE(data_size);
|
||||
uint32_t total_size = kPropNameLength + sizeof(uint32_t) + prop_buf_size;
|
||||
|
||||
// no overflow
|
||||
if (prop_buf_size < data_size || total_size < prop_buf_size) {
|
||||
panic("devicetree integer overflow");
|
||||
}
|
||||
|
||||
// Make sure there's room for the property name and value
|
||||
if ((size_t)(end - buf) < total_size) {
|
||||
panic("devicetree buffer overflow");
|
||||
}
|
||||
|
||||
// Write property name, padding with zeroes to 32 bytes
|
||||
memset(buf, 0, kPropNameLength);
|
||||
strlcpy((char *)buf, name, kPropNameLength);
|
||||
buf += kPropNameLength;
|
||||
|
||||
// Then property size
|
||||
memcpy(buf, &data_size, sizeof(uint32_t));
|
||||
buf += sizeof(uint32_t);
|
||||
|
||||
// And property value, which won't be present if size is 0
|
||||
if (prop_buf_size != 0) {
|
||||
memcpy(buf, data, data_size);
|
||||
// Pad if needed to multiple of 4 bytes
|
||||
if (data_size != prop_buf_size)
|
||||
memset(buf + data_size, 0, prop_buf_size - data_size);
|
||||
buf += prop_buf_size;
|
||||
}
|
||||
|
||||
RELEASE_ASSERT(buf <= end);
|
||||
|
||||
// let caller know how far we wrote
|
||||
*bufptr = buf;
|
||||
}
|
||||
|
||||
static void write_node(dt_node_t *node, uint8_t **bufptr, uint8_t *end)
|
||||
{
|
||||
uint8_t *buf = *bufptr;
|
||||
|
||||
RELEASE_ASSERT(*bufptr < end);
|
||||
|
||||
// Make sure there's room for the node header
|
||||
if ((size_t)(end - buf) < 2 * sizeof(uint32_t))
|
||||
panic("devicetree buffer overflow");
|
||||
// Write the node header (number of properties, then number of children)
|
||||
memcpy(buf, &node->nprops, 4);
|
||||
memcpy(buf + 4, &node->nchildren, 4);
|
||||
buf += 8;
|
||||
|
||||
// Write out each property
|
||||
for (uint32_t i = 0; i < node->nprops; i++) {
|
||||
dtprop_t *prop = &node->props[i];
|
||||
|
||||
write_prop(prop->name, prop->data, prop->size, &buf, end);
|
||||
}
|
||||
|
||||
// Write out all of this node's children
|
||||
for (uint32_t i = 0; i < node->nchildren; i++) {
|
||||
write_node(&node->children[i], &buf, end);
|
||||
}
|
||||
|
||||
// Let the caller know how far we wrote
|
||||
*bufptr = buf;
|
||||
|
||||
RELEASE_ASSERT(*bufptr <= end);
|
||||
}
|
||||
|
||||
void dt_serialize(dt_node_t *dt, void *buffer, size_t bufferlen)
|
||||
{
|
||||
uint8_t *startptr = buffer;
|
||||
uint8_t *endptr = startptr + bufferlen;
|
||||
|
||||
if (dt == NULL)
|
||||
dt = dt_root;
|
||||
|
||||
RELEASE_ASSERT(dt != NULL);
|
||||
|
||||
if (bufferlen < dt_size)
|
||||
panic("devicetree length %zu > buffer size %zu", dt_size, bufferlen);
|
||||
|
||||
write_node(dt, &startptr, endptr);
|
||||
|
||||
// Make sure dt_size is accurate
|
||||
ASSERT(startptr == (uint8_t *)buffer + dt_size);
|
||||
}
|
||||
|
||||
dt_node_t *dt_get_root(void)
|
||||
{
|
||||
return dt_root;
|
||||
}
|
||||
|
||||
bool dt_find_node(dt_node_t *root, const char *path, dt_node_t **node)
|
||||
{
|
||||
uint32_t path_len;
|
||||
const char *rest;
|
||||
|
||||
if (root == NULL) {
|
||||
if (dt_root == NULL)
|
||||
return false;
|
||||
root = dt_root;
|
||||
}
|
||||
|
||||
if(path[0] == '\0') {
|
||||
*node = root;
|
||||
return true;
|
||||
}
|
||||
|
||||
rest = strchr(path, '/');
|
||||
if (rest == NULL) {
|
||||
rest = "";
|
||||
path_len = strlen(path);
|
||||
} else {
|
||||
path_len = rest - path;
|
||||
rest++;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < root->nchildren; i++) {
|
||||
dt_node_t *child = &root->children[i];
|
||||
if (child->name == NULL) {
|
||||
char *prop_name = "name";
|
||||
if (!dt_get_prop(child, &prop_name, &child->name, &child->name_size)) {
|
||||
dprintf(DEBUG_CRITICAL, "dt: found node with no name: %p\n", child);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (child->name_size == path_len + 1 && memcmp(path, child->name, path_len) == 0) {
|
||||
return dt_find_node(child, rest, node);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool find_prop(dt_node_t *node, const char *name, dtprop_t **prop_out)
|
||||
{
|
||||
ASSERT(node != NULL);
|
||||
ASSERT(name != NULL);
|
||||
|
||||
for (uint32_t i = 0; i < node->nprops; i++) {
|
||||
dtprop_t *prop = &node->props[i];
|
||||
|
||||
if (strncmp(name, prop->name, kPropNameLength) == 0) {
|
||||
if (prop_out != NULL)
|
||||
*prop_out = prop;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dt_get_prop(dt_node_t *node, char **prop_name, void **prop_data, uint32_t *prop_size)
|
||||
{
|
||||
dtprop_t *prop;
|
||||
|
||||
ASSERT(node != NULL);
|
||||
ASSERT(prop_name != NULL && *prop_name != NULL);
|
||||
|
||||
if(find_prop(node, *prop_name, &prop)) {
|
||||
*prop_name = prop->name;
|
||||
if (prop_size != NULL)
|
||||
*prop_size = prop->size;
|
||||
if (prop_data != NULL)
|
||||
*prop_data = prop->data;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Like dt_find_prop, but with a less obnoxious interface on prop_name
|
||||
bool dt_find_prop(dt_node_t *node, const char *prop_name, void **prop_data, uint32_t *prop_size)
|
||||
{
|
||||
dtprop_t *prop;
|
||||
|
||||
ASSERT(node != NULL);
|
||||
ASSERT(prop_name != NULL);
|
||||
|
||||
if(find_prop(node, prop_name, &prop)) {
|
||||
if (prop_size != NULL)
|
||||
*prop_size = prop->size;
|
||||
if (prop_data != NULL)
|
||||
*prop_data = prop->data;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool dt_has_prop(dt_node_t *node, const char *prop_name)
|
||||
{
|
||||
return dt_find_prop(node, prop_name, NULL, NULL);
|
||||
}
|
||||
|
||||
const char *dt_get_node_name(dt_node_t *node)
|
||||
{
|
||||
if (node->name == NULL) {
|
||||
char *prop_name = "name";
|
||||
dt_get_prop(node, &prop_name, &node->name, &node->name_size);
|
||||
}
|
||||
|
||||
return node->name;
|
||||
}
|
||||
|
||||
static dtprop_t *add_prop(dt_node_t *node, const char *name)
|
||||
{
|
||||
uint32_t nprops = node->nprops + 1;
|
||||
dtprop_t *prop;
|
||||
|
||||
ASSERT(strlen(name) < kPropNameLength);
|
||||
|
||||
node->props = realloc(node->props, nprops * sizeof(*node->props));
|
||||
|
||||
prop = &node->props[nprops - 1];
|
||||
memset(prop, 0, sizeof(*prop));
|
||||
|
||||
prop->name = calloc(kPropNameLength, 1);
|
||||
strlcpy(prop->name, name, kPropNameLength);
|
||||
prop->name_malloced = true;
|
||||
|
||||
node->nprops = nprops;
|
||||
|
||||
// The device tree will grow by the size of the property name and length fields
|
||||
// The caller of this function will account for the growth from the value
|
||||
dt_size += 4 + kPropNameLength;
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
void dt_set_prop(dt_node_t *node, const char *name, const void *data, uint32_t size)
|
||||
{
|
||||
dtprop_t *prop;
|
||||
size_t cur_alloc_size;
|
||||
size_t new_alloc_size;
|
||||
|
||||
ASSERT(node != NULL);
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(size == 0 || data != NULL);
|
||||
ASSERT(!dt_sealed);
|
||||
|
||||
if (!find_prop(node, name, &prop))
|
||||
prop = add_prop(node, name);
|
||||
|
||||
// if we're changing the name, get rid of the cache
|
||||
if (prop->data == node->name) {
|
||||
node->name = NULL;
|
||||
node->name_size = 0;
|
||||
}
|
||||
|
||||
// Data is always serialized with length padded to 4 bytes
|
||||
new_alloc_size = ROUND_SIZE(size);
|
||||
cur_alloc_size = ROUND_SIZE(prop->size);
|
||||
|
||||
// zero-length properties are legal, but malloc(0) isn't
|
||||
// rest of this file handles prop.data == NULL and prop.size == 0 properly
|
||||
if (new_alloc_size > 0) {
|
||||
// Only re-allocate if needed. data_malloced will be false for
|
||||
// newly created properties because add_prop zeroes the struct
|
||||
// before returning it
|
||||
if (cur_alloc_size < new_alloc_size) {
|
||||
if (!prop->data_malloced)
|
||||
prop->data = malloc(new_alloc_size);
|
||||
else
|
||||
prop->data = realloc(prop->data, new_alloc_size);
|
||||
prop->data_malloced = true;
|
||||
}
|
||||
memcpy(prop->data, data, size);
|
||||
// zero-fill any padding bytes
|
||||
memset((uint8_t *)prop->data + size, 0, new_alloc_size - size);
|
||||
}
|
||||
|
||||
prop->size = size;
|
||||
|
||||
// add_prop accounted for the name and length fields, we just need
|
||||
// to fgure out the difference from the value. Since add_prop zeroes
|
||||
// out the struct, for new properties, cur_alloc_size will be 0
|
||||
dt_size += new_alloc_size - cur_alloc_size;
|
||||
}
|
||||
|
||||
void dt_set_prop_32(dt_node_t *node, const char *name, uint32_t value)
|
||||
{
|
||||
dt_set_prop(node, name, &value, sizeof(value));
|
||||
}
|
||||
|
||||
void dt_set_prop_64(dt_node_t *node, const char *name, uint64_t value)
|
||||
{
|
||||
dt_set_prop(node, name, &value, sizeof(value));
|
||||
}
|
||||
|
||||
void dt_set_prop_addr(dt_node_t *node, const char *name, uintptr_t value)
|
||||
{
|
||||
dt_set_prop(node, name, &value, sizeof(value));
|
||||
}
|
||||
|
||||
void dt_set_prop_str(dt_node_t *node, const char *name, const char *str)
|
||||
{
|
||||
dt_set_prop(node, name, str, strlen(str) + 1);
|
||||
}
|
||||
|
||||
bool dt_remove_prop(dt_node_t *node, const char *name)
|
||||
{
|
||||
ASSERT(node != NULL);
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(!dt_sealed);
|
||||
|
||||
for (uint32_t i = 0; i < node->nprops; i++) {
|
||||
dtprop_t *prop = &node->props[i];
|
||||
|
||||
if (strncmp(name, prop->name, kPropNameLength) == 0) {
|
||||
// unlikely, but if we're removing the name, get rid of the cache
|
||||
if (prop->data == node->name) {
|
||||
node->name = NULL;
|
||||
node->name_size = 0;
|
||||
}
|
||||
|
||||
dt_size -= ROUND_SIZE(prop->size) + 4 + kPropNameLength;
|
||||
|
||||
memmove(prop, prop + 1, (node->nprops - i - 1) * sizeof(*prop));
|
||||
node->nprops--;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dt_rename_prop(dt_node_t *node, const char *name, const char *new_name)
|
||||
{
|
||||
ASSERT(node != NULL);
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(new_name != NULL);
|
||||
ASSERT(!dt_sealed);
|
||||
|
||||
dtprop_t *prop;
|
||||
size_t new_name_len = strlen(new_name);
|
||||
|
||||
ASSERT(new_name_len < kPropNameLength - 1);
|
||||
|
||||
if (find_prop(node, name, &prop)) {
|
||||
// unlikely, but if we're renaming the name property, get rid of the cache
|
||||
if (prop->data == node->name) {
|
||||
node->name = NULL;
|
||||
node->name_size = 0;
|
||||
}
|
||||
|
||||
memset(prop->name, 0, kPropNameLength);
|
||||
memcpy(prop->name, new_name, new_name_len);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void dt_seal(void)
|
||||
{
|
||||
dt_sealed = true;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include <sys/menu.h>
|
||||
#include "devicetree_private.h"
|
||||
|
||||
void print_indent(int level)
|
||||
{
|
||||
for (int i = 0; i < level; i++)
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
void print_property(struct dtprop *prop, bool verbose)
|
||||
{
|
||||
uint32_t prop_size;
|
||||
bool truncated = false;
|
||||
|
||||
printf("%s [0x%x]:", prop->name, prop->size);
|
||||
|
||||
prop_size = prop->size;
|
||||
if (!verbose && prop_size > 16) {
|
||||
prop_size = 16;
|
||||
truncated = true;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < prop_size; i++)
|
||||
printf(" %02x", ((uint8_t *)prop->data)[i]);
|
||||
|
||||
if (truncated)
|
||||
printf(" ...");
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void dump_tree_int(dt_node_t *node, int indent, bool verbose)
|
||||
{
|
||||
const char *node_name;
|
||||
|
||||
node_name = dt_get_node_name(node);
|
||||
if (node_name == NULL)
|
||||
node_name = "<UNNAMED>";
|
||||
|
||||
print_indent(indent);
|
||||
printf("%s {\n", node_name);
|
||||
indent += 4;
|
||||
|
||||
for (unsigned i = 0; i < node->nprops; i++) {
|
||||
print_indent(indent);
|
||||
print_property(&node->props[i], verbose);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < node->nchildren; i++)
|
||||
dump_tree_int(&node->children[i], indent, verbose);
|
||||
|
||||
indent -= 4;
|
||||
print_indent(indent);
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
#if DEBUG_BUILD
|
||||
void dt_dump(bool verbose)
|
||||
{
|
||||
dump_tree_int(dt_get_root(), 0, verbose);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2015 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include <lib/env.h>
|
||||
#include <lib/fs.h>
|
||||
#include <lib/image.h>
|
||||
#include <lib/mib.h>
|
||||
#include <sys/menu.h>
|
||||
#include <string.h>
|
||||
|
||||
/* load the device tree out of flash into a heap-based piece of memory */
|
||||
int dt_load(void)
|
||||
{
|
||||
int result = 0;
|
||||
addr_t dtaddr;
|
||||
size_t dtsize;
|
||||
|
||||
/* load to default addresses */
|
||||
dtaddr = mib_get_addr(kMIBTargetDefaultDeviceTreeAddress);
|
||||
dtsize = mib_get_size(kMIBTargetDefaultDeviceTreeSize);
|
||||
|
||||
/* if we don't already have a devicetree, look for a known image containing one */
|
||||
if (dt_get_size() == 0) {
|
||||
|
||||
if (image_load_type(&dtaddr, &dtsize, IMAGE_TYPE_DEVTREE, 0) != 0) {
|
||||
dprintf(DEBUG_INFO, "load_device_tree: failed to find device tree\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dt_deserialize((void *)dtaddr, dtsize)) {
|
||||
dprintf(DEBUG_CRITICAL, "Mal-formed devicetree\n");
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* consolidate environment */
|
||||
security_consolidate_environment();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dt_load_file(const char *path)
|
||||
{
|
||||
int result = 0;
|
||||
u_int32_t type;
|
||||
addr_t dtaddr;
|
||||
size_t dtsize;
|
||||
|
||||
/* load to default addresses */
|
||||
dtaddr = mib_get_addr(kMIBTargetDefaultDeviceTreeAddress);
|
||||
dtsize = mib_get_size(kMIBTargetDefaultDeviceTreeSize);
|
||||
|
||||
// Image3 requires the non-restore types
|
||||
if (mib_get_u32(kMIBPlatformImageFormat) == 4) {
|
||||
// Image4
|
||||
type = IMAGE_TYPE_DEVTREE_RESTORE;
|
||||
} else {
|
||||
// Image3
|
||||
type = IMAGE_TYPE_DEVTREE;
|
||||
}
|
||||
|
||||
if (image_load_file(path, &dtaddr, &dtsize, &type, 1, NULL, 0) != 0) {
|
||||
dprintf(DEBUG_INFO, "failed to load devicetree from %s", path);
|
||||
dt_init();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dt_deserialize((void *)dtaddr, dtsize)) {
|
||||
dprintf(DEBUG_CRITICAL, "Mal-formed devicetree\n");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
/* consolidate environment */
|
||||
security_consolidate_environment();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_devicetree(int argc, struct cmd_arg *argv)
|
||||
{
|
||||
addr_t addr = mib_get_addr(kMIBTargetDefaultLoadAddress);
|
||||
addr_t dtaddr = mib_get_addr(kMIBTargetDefaultDeviceTreeAddress);
|
||||
size_t dtsize = mib_get_size(kMIBTargetDefaultDeviceTreeSize);
|
||||
size_t len;
|
||||
u_int32_t type;
|
||||
|
||||
len = env_get_uint("filesize", 0);
|
||||
|
||||
#if !RELEASE_BUILD
|
||||
if ((argc > 3) || ((argc > 1) && !strcmp("help", argv[1].str))) {
|
||||
printf("usage:\n\t%s [<len>] [<address>]\n", argv[0].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = env_get_uint("loadaddr", addr);
|
||||
|
||||
if (argc > 1)
|
||||
len = argv[1].u;
|
||||
if (argc > 2)
|
||||
addr = argv[2].u;
|
||||
#endif
|
||||
|
||||
if (len == 0) {
|
||||
printf("filesize variable invalid or not set, aborting\n");
|
||||
return -1;
|
||||
}
|
||||
if (len > dtsize) {
|
||||
printf("Device Tree too large\n");
|
||||
return -1;
|
||||
}
|
||||
if (!security_allow_memory((void *)addr, len)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Image3 requires the non-restore types
|
||||
if (mib_get_u32(kMIBPlatformImageFormat) == 4) {
|
||||
// Image4
|
||||
type = IMAGE_TYPE_DEVTREE_RESTORE;
|
||||
} else {
|
||||
// Image3
|
||||
type = IMAGE_TYPE_DEVTREE;
|
||||
}
|
||||
|
||||
if (image_load_memory(addr, len, &dtaddr, &dtsize, &type, 1, NULL, 0) != 0) {
|
||||
printf("Device Tree image not valid\n");
|
||||
dt_init();
|
||||
return -1;
|
||||
}
|
||||
|
||||
dt_deserialize((void *)dtaddr, dtsize);
|
||||
|
||||
/* consolidate environment */
|
||||
security_consolidate_environment();
|
||||
|
||||
printf("loaded device tree at %p of size 0x%zx, from image at %p\n", (void *)dtaddr, dtsize, (void *)addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef DEVICETREE_PRIVATE_H
|
||||
#define DEVICETREE_PRIVATE_H
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DT_PROP_FLAG_PLACEHOLDER (1 << 31)
|
||||
#define DT_PROP_FLAGS_MASK (0xf0000000)
|
||||
#define DT_PROP_SIZE_MASK (~DT_PROP_FLAGS_MASK)
|
||||
|
||||
typedef struct dtprop {
|
||||
char *name;
|
||||
void *data;
|
||||
uint32_t flags;
|
||||
uint32_t size;
|
||||
bool name_malloced;
|
||||
bool data_malloced;
|
||||
} dtprop_t;
|
||||
|
||||
struct dtnode {
|
||||
dtprop_t *props;
|
||||
dt_node_t *children;
|
||||
uint32_t nprops;
|
||||
uint32_t nchildren;
|
||||
// caches name property
|
||||
void *name;
|
||||
// caches size of name property
|
||||
uint32_t name_size;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <unittest.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <lib/env.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include <mock_syscfg.h>
|
||||
|
||||
struct blockdev *create_mock_blockdev(const char *name, const char *filename, uint32_t block_size);
|
||||
|
||||
int do_printenv(int argc, void *args);
|
||||
|
||||
int fuzz_main(const char *filename)
|
||||
{
|
||||
void *dt_in_buffer;
|
||||
void *dt_out_buffer;
|
||||
size_t dt_in_size;
|
||||
size_t dt_out_size;
|
||||
struct stat st;
|
||||
FILE *f;
|
||||
int result;
|
||||
dt_node_t *root;
|
||||
|
||||
// Set up environment
|
||||
mock_syscfg_add('ABCD', "abcd", 4);
|
||||
mock_syscfg_add('AAAA', "abcdabcdabcdabcd", 16);
|
||||
mock_syscfg_add('AAAB', "abcdabcdabcdabc", 15);
|
||||
mock_syscfg_add('AAAC', "abcdabcdabcdabcda", 17);
|
||||
|
||||
env_set("a", "", 0);
|
||||
env_set("b", "b", 0);
|
||||
env_set("c", "cc", 0);
|
||||
env_set("d", "ddd", 0);
|
||||
env_set("e", "eeee", 0);
|
||||
env_set("f", "fffff", 0);
|
||||
env_set("g", "ggggggggggggggggggggggggggggggggggggggggggg", 0);
|
||||
env_set("h", "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", 0);
|
||||
env_set("i", "01:02:03:40:50:60", 0);
|
||||
|
||||
// Get input size
|
||||
result = stat(filename, &st);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
dt_in_size = st.st_size;
|
||||
|
||||
// Read it
|
||||
f = fopen(filename, "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
|
||||
dt_in_buffer = malloc(dt_in_size);
|
||||
result = fread(dt_in_buffer, 1, dt_in_size, f);
|
||||
TEST_ASSERT_EQ(result, dt_in_size);
|
||||
|
||||
root = dt_deserialize(dt_in_buffer, dt_in_size);
|
||||
|
||||
if (root != NULL) {
|
||||
// serialize the unmodified devicetree
|
||||
dt_out_size = dt_get_size();
|
||||
dt_out_buffer = malloc(dt_out_size);
|
||||
dt_serialize(root, dt_out_buffer, dt_out_size);
|
||||
free(dt_out_buffer);
|
||||
|
||||
// make some mods
|
||||
dt_set_prop(root, "a", NULL, 0);
|
||||
dt_set_prop(root, "b", "abcdefg", 7);
|
||||
dt_set_prop(root, "c", "abcdefgh", 8);
|
||||
dt_set_prop(root, "d", "abcdefghi", 9);
|
||||
dt_remove_prop(root, "e");
|
||||
dt_rename_prop(root, "f", "z");
|
||||
|
||||
dt_node_t *node;
|
||||
dt_find_node(root, "chosen", &node);
|
||||
dt_find_node(NULL, "chosen", &node);
|
||||
dt_find_node(root, "product/camera", &node);
|
||||
dt_find_node(NULL, "product/camera", &node);
|
||||
dt_find_node(root, "product/sdfasd", &node);
|
||||
|
||||
// serialize with the modifications
|
||||
dt_out_size = dt_get_size();
|
||||
dt_out_buffer = malloc(dt_out_size);
|
||||
dt_serialize(root, dt_out_buffer, dt_out_size);
|
||||
free(dt_out_buffer);
|
||||
|
||||
// causes deserialized devicetree to be freed
|
||||
dt_init();
|
||||
}
|
||||
|
||||
free(dt_in_buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Mock for lib/env
|
||||
bool
|
||||
env_blacklist(const char *name, bool write)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
env_blacklist_nvram(const char *name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# AFL fuzzing dictionary for device tree
|
||||
#
|
||||
# See docs/fuzzing.txt
|
||||
#
|
||||
# Pass to afl-fuzz with -x option
|
||||
|
||||
zeroes0="zeroes/0"
|
||||
zeroes1="zeroes/1"
|
||||
zeroes2="zeroes/2"
|
||||
zeroes3="zeroes/3"
|
||||
zeroes4="zeroes/4"
|
||||
zeroes5="zeroes/5"
|
||||
zeroes7="zeroes/7"
|
||||
zeroes500="zeroes/500"
|
||||
syscfgAAAA="syscfg/AAAA"
|
||||
syscfgAAAB="syscfg/AAAB"
|
||||
syscfgAAAC="syscfg/AAAC"
|
||||
syscfgABCD="syscfg/ABCD"
|
||||
macaddr="macaddr/i"
|
||||
macaddrA="macaddr/a"
|
||||
macaddrB="macaddr/b"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (C) 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.
|
||||
#
|
||||
|
||||
LIBDEVICETREE_DIR := $(GET_LOCAL_DIR)
|
||||
LIBDEVICETREE_BUILD := $(call TOLIBDIR,$(LIBDEVICETREE_DIR)/LIBDEVICETREE.a)
|
||||
COMMONLIBS += LIBDEVICETREE
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
LIBDEVICETREE_OBJS := $(LIBDEVICETREE_DIR)/devicetree.o \
|
||||
$(LIBDEVICETREE_DIR)/devicetree_load.o \
|
||||
$(LIBDEVICETREE_DIR)/devicetree_debug.o
|
||||
|
||||
LIBDEVICETREE_OBJS := $(call TOLIBOBJDIR,$(LIBDEVICETREE_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBDEVICETREE_OBJS:%o=%d)
|
||||
|
||||
$(LIBDEVICETREE_BUILD): $(LIBDEVICETREE_OBJS)
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2007, 2010, 2015 Apple, Inc. All rights reserved.
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple, Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple, Inc.
|
||||
#
|
||||
|
||||
OPTIONS += WITH_DEVICETREE=1
|
||||
LIBRARY_MODULES += lib/devicetree
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := devicetree-fuzz
|
||||
|
||||
TEST_OBJS := \
|
||||
$(LOCAL_DIR)/devicetree.o \
|
||||
lib/env/env.o
|
||||
|
||||
TEST_SUPPORT_OBJS += \
|
||||
tests/fuzz-main.o \
|
||||
tests/mocks/syscfg.o \
|
||||
$(LOCAL_DIR)/fuzz.o
|
||||
|
||||
@@ -0,0 +1,876 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <unittest.h>
|
||||
#include <mock_syscfg.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include <lib/env.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
typedef struct dt_test_data {
|
||||
void *input;
|
||||
const void *expected;
|
||||
size_t input_size;
|
||||
size_t expected_size;
|
||||
uint32_t misc;
|
||||
} dt_test_data;
|
||||
|
||||
#define DT_TESTCASE(in, out) { \
|
||||
.input = (void *)in, \
|
||||
.input_size = sizeof(in) - 1, \
|
||||
.expected = out, \
|
||||
.expected_size = sizeof(out) - 1, \
|
||||
.misc = 0 \
|
||||
}
|
||||
|
||||
#define DT_TESTCASE_MISC(in, out, misc) { \
|
||||
.input = in, \
|
||||
.input_size = sizeof(in) - 1, \
|
||||
.expected = out, \
|
||||
.expected_size = sizeof(out) - 1, \
|
||||
.misc = misc \
|
||||
}
|
||||
|
||||
#define DT_TESTCASE_FAIL(in) { \
|
||||
.input = in, \
|
||||
.input_size = sizeof(in) - 1, \
|
||||
.expected = NULL, \
|
||||
.expected_size = 0, \
|
||||
.misc = 0 \
|
||||
}
|
||||
|
||||
#define DT_TESTCASE_FAIL_MISC(in, misc) { \
|
||||
.input = in, \
|
||||
.input_size = sizeof(in) - 1, \
|
||||
.expected = NULL, \
|
||||
.expected_size = 0, \
|
||||
.misc = misc \
|
||||
}
|
||||
|
||||
// handy for dumping two buffers side-by-side when tests fail
|
||||
static void hexdump2(uint8_t *buf1, uint8_t *buf2, size_t size) __unused;
|
||||
static void hexdump2(uint8_t *buf1, uint8_t *buf2, size_t size) {
|
||||
for (size_t i = 0; i < size; i += 8) {
|
||||
printf("%04zx: ", i);
|
||||
for (size_t j = 0; j < 8; j++) {
|
||||
if (i + j < size)
|
||||
printf(" %02x", buf1[i + j]);
|
||||
else
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
|
||||
for (size_t j = 0; j < 8; j++) {
|
||||
if (i + j < size)
|
||||
printf(" %02x", buf2[i + j]);
|
||||
else
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
|
||||
for (size_t j = 0; j < 8; j++) {
|
||||
if (i + j < size) {
|
||||
uint8_t c = buf1[i + j];
|
||||
if (!isprint(c))
|
||||
c = '.';
|
||||
printf("%c", c);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
|
||||
for (size_t j = 0; j < 8; j++) {
|
||||
if (i + j < size) {
|
||||
uint8_t c = buf2[i + j];
|
||||
if (!isprint(c))
|
||||
c = '.';
|
||||
printf("%c", c);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
dt_node_t *test_common_deserialize(dt_test_data *data)
|
||||
{
|
||||
dt_init();
|
||||
|
||||
return dt_deserialize((void *)data->input, data->input_size);
|
||||
}
|
||||
|
||||
void test_common_serialize(dt_test_data *data)
|
||||
{
|
||||
void *serialize_buf;
|
||||
|
||||
TEST_ASSERT_EQ(dt_get_size(), data->expected_size);
|
||||
|
||||
serialize_buf = calloc(data->expected_size, 1);
|
||||
dt_serialize(0, serialize_buf, data->expected_size);
|
||||
TEST_ASSERT_MEM_EQ(serialize_buf, data->expected, data->expected_size);
|
||||
free(serialize_buf);
|
||||
}
|
||||
|
||||
uint8_t test_dt1[] =
|
||||
// root node
|
||||
"\x01\x00\x00\x00" // 1 property
|
||||
"\x03\x00\x00\x00" // 3 children
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 1st child
|
||||
"\x03\x00\x00\x00" // 3 properties
|
||||
"\x00\x00\x00\x00" // 0 children
|
||||
|
||||
// 1st property of 1st child
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x07\x00\x00\x00" // property length 7
|
||||
"child1\0\0"
|
||||
|
||||
// 2nd property of 1st child
|
||||
"property1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x09\x00\x00\x00" // property length 9
|
||||
"abcdefghi\0\0\0"
|
||||
|
||||
// 3rd property of 1st child
|
||||
"property2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x08\x00\x00\x00" // property length 8
|
||||
"01234567"
|
||||
|
||||
// 2nd child
|
||||
"\x03\x00\x00\x00" // 3 properties
|
||||
"\x01\x00\x00\x00" // 1 child
|
||||
|
||||
// 1st property of 2nd child
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x07\x00\x00\x00" // property length 7
|
||||
"child2\0\0"
|
||||
|
||||
// 2nd property of 2nd child
|
||||
"property1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x00\x00\x00\x00" // property length 0
|
||||
""
|
||||
|
||||
// 3rd property of 2nd child
|
||||
"property2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x01\x00\x00\x00" // property length 1
|
||||
"X\x00\x00\x00"
|
||||
|
||||
// 1st grandchild
|
||||
"\x02\x00\x00\x00" // 2 properties
|
||||
"\x00\x00\x00\x00" // 0 children
|
||||
|
||||
// 1st property of 1st grandchild
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0b\x00\x00\x00" // property length 11
|
||||
"grandchild\x00\x00" //
|
||||
|
||||
// 2nd property of 1st grandchild
|
||||
"theprop\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"YYYY"
|
||||
|
||||
// 3rd child
|
||||
"\x01\x00\x00\x00" // 1 property
|
||||
"\x00\x00\x00\x00" // 0 children
|
||||
|
||||
// 1st property of 3rd child
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x07\x00\x00\x00" // property length 7
|
||||
"child3\0\0";
|
||||
|
||||
void test_find_property(dt_node_t *node, char *propName, void *expected, uint32_t expected_len)
|
||||
{
|
||||
char *propNameInOut = propName;
|
||||
void *propData;
|
||||
uint32_t propSize;
|
||||
|
||||
if (expected != NULL) {
|
||||
TEST_ASSERT_EQ(FindProperty(node, &propNameInOut, &propData, &propSize), true);
|
||||
// result should point into the device tree's backing store
|
||||
TEST_ASSERT_PTR_NEQ(propNameInOut, propName);
|
||||
TEST_ASSERT_MEM_EQ(propData, expected, expected_len);
|
||||
TEST_ASSERT_EQ(propSize, expected_len);
|
||||
} else {
|
||||
TEST_ASSERT_EQ(FindProperty(node, &propNameInOut, &propData, &propSize), false);
|
||||
}
|
||||
}
|
||||
|
||||
void test_devicetree_legacy(uintptr_t param)
|
||||
{
|
||||
dt_node_t *node;
|
||||
dt_node_t *root;
|
||||
dt_node_t *child1, *child2, *child3, *grandchild;
|
||||
|
||||
dt_init();
|
||||
|
||||
// - 1 for null terminator of the string
|
||||
dt_deserialize(&test_dt1[0], sizeof(test_dt1) - 1);
|
||||
|
||||
// we should be able to find the root of the device tree
|
||||
TEST_ASSERT_EQ(FindNode(0, "", &root), true);
|
||||
|
||||
// An empty path returns the same node
|
||||
TEST_ASSERT_EQ(FindNode(root, "", &node), true);
|
||||
TEST_ASSERT_PTR_EQ(root, node);
|
||||
|
||||
// none of these should be found
|
||||
TEST_ASSERT_EQ(FindNode(0, "root", &node), false);
|
||||
TEST_ASSERT_EQ(FindNode(0, "/", &node), false);
|
||||
TEST_ASSERT_EQ(FindNode(0, "child0", &node), false);
|
||||
TEST_ASSERT_EQ(FindNode(0, "child1/grandchild", &node), false);
|
||||
TEST_ASSERT_EQ(FindNode(0, "child2/grandchild2", &node), false);
|
||||
TEST_ASSERT_EQ(FindNode(0, "child2/grandchild/child", &node), false);
|
||||
|
||||
TEST_ASSERT_EQ(FindNode(0, "child1", &child1), true);
|
||||
TEST_ASSERT_NOT_NULL(child1);
|
||||
test_find_property(child1, "name", "child1", strlen("child1") + 1);
|
||||
test_find_property(child1, "property1", "abcdefghi", 9);
|
||||
test_find_property(child1, "property2", "01234567", 8);
|
||||
test_find_property(child1, "someprop", NULL, 0);
|
||||
|
||||
TEST_ASSERT_EQ(FindNode(0, "child2", &child2), true);
|
||||
TEST_ASSERT_NOT_NULL(child2);
|
||||
test_find_property(child2, "name", "child2", strlen("child2") + 1);
|
||||
test_find_property(child2, "property1", "", 0);
|
||||
test_find_property(child2, "property2", "X", 1);
|
||||
test_find_property(child2, "someprop", NULL, 0);
|
||||
|
||||
TEST_ASSERT_EQ(FindNode(0, "child3", &child3), true);
|
||||
TEST_ASSERT_NOT_NULL(child3);
|
||||
test_find_property(child3, "name", "child3", strlen("child3") + 1);
|
||||
test_find_property(child3, "property1", NULL, 0);
|
||||
test_find_property(child3, "property2", NULL, 0);
|
||||
|
||||
TEST_ASSERT_EQ(FindNode(0, "child2/grandchild", &grandchild), true);
|
||||
TEST_ASSERT_NOT_NULL(grandchild);
|
||||
test_find_property(grandchild, "name", "grandchild", strlen("grandchild") + 1);
|
||||
test_find_property(grandchild, "theprop", "YYYY", 4);
|
||||
}
|
||||
|
||||
void test_dt_get_prop(dt_node_t *node, char *propName, void *expected, uint32_t expected_len)
|
||||
{
|
||||
char *propNameInOut = propName;
|
||||
void *propData;
|
||||
uint32_t propSize;
|
||||
|
||||
if (expected != NULL) {
|
||||
TEST_ASSERT_EQ(dt_get_prop(node, &propNameInOut, &propData, &propSize), true);
|
||||
// result should point into the device tree's backing store
|
||||
TEST_ASSERT_PTR_NEQ(propNameInOut, propName);
|
||||
TEST_ASSERT_MEM_EQ(propData, expected, expected_len);
|
||||
TEST_ASSERT_EQ(propSize, expected_len);
|
||||
} else {
|
||||
TEST_ASSERT_EQ(dt_get_prop(node, &propNameInOut, &propData, &propSize), false);
|
||||
}
|
||||
}
|
||||
|
||||
void test_dt_deserialize(uintptr_t context)
|
||||
{
|
||||
dt_node_t *node;
|
||||
dt_node_t *root;
|
||||
dt_node_t *child1, *child2, *child3, *grandchild;
|
||||
|
||||
dt_init();
|
||||
|
||||
// - 1 for null terminator of the string
|
||||
root = dt_deserialize(&test_dt1[0], sizeof(test_dt1) - 1);
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
|
||||
// size should match the size of the source buffer
|
||||
TEST_ASSERT_EQ(dt_get_size(), sizeof(test_dt1) - 1);
|
||||
|
||||
// we should be able to find the root of the device tree
|
||||
TEST_ASSERT_PTR_EQ(dt_get_root(), root);
|
||||
|
||||
// An empty path returns the same node
|
||||
TEST_ASSERT_EQ(dt_find_node(root, "", &node), true);
|
||||
TEST_ASSERT_PTR_EQ(root, node);
|
||||
|
||||
// none of these should be found
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "root", &node), false);
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "/", &node), false);
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "child0", &node), false);
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "child1/grandchild", &node), false);
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "child2/grandchild2", &node), false);
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "child2/grandchild/child", &node), false);
|
||||
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "child1", &child1), true);
|
||||
TEST_ASSERT_NOT_NULL(child1);
|
||||
test_dt_get_prop(child1, "name", "child1", strlen("child1") + 1);
|
||||
test_dt_get_prop(child1, "property1", "abcdefghi", 9);
|
||||
test_dt_get_prop(child1, "property2", "01234567", 8);
|
||||
test_dt_get_prop(child1, "someprop", NULL, 0);
|
||||
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "child2", &child2), true);
|
||||
TEST_ASSERT_NOT_NULL(child2);
|
||||
test_dt_get_prop(child2, "name", "child2", strlen("child2") + 1);
|
||||
test_dt_get_prop(child2, "property1", "", 0);
|
||||
test_dt_get_prop(child2, "property2", "X", 1);
|
||||
test_dt_get_prop(child2, "someprop", NULL, 0);
|
||||
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "child3", &child3), true);
|
||||
TEST_ASSERT_NOT_NULL(child3);
|
||||
test_dt_get_prop(child3, "name", "child3", strlen("child3") + 1);
|
||||
test_dt_get_prop(child3, "property1", NULL, 0);
|
||||
test_dt_get_prop(child3, "property2", NULL, 0);
|
||||
|
||||
TEST_ASSERT_EQ(dt_find_node(0, "child2/grandchild", &grandchild), true);
|
||||
TEST_ASSERT_NOT_NULL(grandchild);
|
||||
test_dt_get_prop(grandchild, "name", "grandchild", strlen("grandchild") + 1);
|
||||
test_dt_get_prop(grandchild, "theprop", "YYYY", 4);
|
||||
}
|
||||
|
||||
// Deserialize and then reserialize device tree,
|
||||
// verify output is identical to input
|
||||
void test_dt_serialize(uintptr_t context)
|
||||
{
|
||||
dt_test_data data = DT_TESTCASE(test_dt1, test_dt1);
|
||||
|
||||
test_common_deserialize(&data);
|
||||
test_common_serialize(&data);
|
||||
}
|
||||
|
||||
// Deserialize and then reserialize device tree using a buffer
|
||||
// too small for the result, verify that this results in a panic
|
||||
void test_dt_serialize_too_small(uintptr_t context)
|
||||
{
|
||||
size_t out_buffer_size;
|
||||
void *serialize_buf;
|
||||
|
||||
dt_test_data data = DT_TESTCASE(test_dt1, test_dt1);
|
||||
|
||||
test_common_deserialize(&data);
|
||||
dt_init();
|
||||
|
||||
|
||||
if ((int)context >= 0) {
|
||||
out_buffer_size = context;
|
||||
} else {
|
||||
out_buffer_size = data.expected_size - (size_t)(-1 * (int)context);
|
||||
}
|
||||
serialize_buf = calloc(out_buffer_size, 1);
|
||||
|
||||
TEST_EXPECT_PANIC();
|
||||
|
||||
dt_serialize(0, serialize_buf, out_buffer_size);
|
||||
free(serialize_buf);
|
||||
|
||||
TEST_EXPECT_PANICKED();
|
||||
}
|
||||
|
||||
uint8_t test_dt2[] =
|
||||
// root node
|
||||
"\x02\x00\x00\x00" // 2 properties
|
||||
"\x01\x00\x00\x00" // 1 child
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 2nd property of root
|
||||
"prop\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"QWER"
|
||||
|
||||
// 1st child
|
||||
"\x03\x00\x00\x00" // 3 properties
|
||||
"\x00\x00\x00\x00" // 0 children
|
||||
|
||||
// 1st property of 1st child
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x07\x00\x00\x00" // property length 7
|
||||
"child1\0\0"
|
||||
|
||||
// 2nd property of 1st child
|
||||
"prop\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"ASDF"
|
||||
|
||||
// 3rd property of 1st child
|
||||
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x00\x00\x00\x00"; // property length 0
|
||||
|
||||
uint8_t test_dt2_out[] =
|
||||
// root node
|
||||
"\x01\x00\x00\x00" // 1 property
|
||||
"\x01\x00\x00\x00" // 1 child
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 1st child
|
||||
"\x09\x00\x00\x00" // 9 properties
|
||||
"\x00\x00\x00\x00" // 0 children
|
||||
|
||||
// 1st property of 1st child
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0e\x00\x00\x00" // property length 14
|
||||
"renamed-child\0\0\0"
|
||||
|
||||
// 2nd property of 1st child
|
||||
"renamed-prop\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"ASDF"
|
||||
|
||||
// 3rd property of 1st child
|
||||
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x00\x00\x00\x00" // property length 0
|
||||
|
||||
// 4th property of 1st child
|
||||
"newprop32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"\xcd\xab\x34\x12"
|
||||
|
||||
// 5th property of 1st child
|
||||
"newprop64\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x08\x00\x00\x00" // property length 8
|
||||
"\xef\xcd\xab\x90\x78\x56\x34\x12"
|
||||
|
||||
// 6th property of 1st child
|
||||
"newpropaddr\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x08\x00\x00\x00" // property length 8
|
||||
"\x88\x77\x66\x55\x44\x33\x22\x11"
|
||||
|
||||
// 7th property of 1st child
|
||||
"newpropstr\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x08\x00\x00\x00" // property length 8
|
||||
"strprop\0"
|
||||
|
||||
// 8th property of 1st child
|
||||
"newpropempty\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x00\x00\x00\x00" // property length 0
|
||||
|
||||
// 9th property of 1st child
|
||||
"newpropdata\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0b\x00\x00\x00" // property length 11
|
||||
"somedata\0xy\0";
|
||||
|
||||
void test_dt_update(uintptr_t context)
|
||||
{
|
||||
char *prop_name;
|
||||
dt_node_t *root;
|
||||
dt_node_t *node;
|
||||
|
||||
dt_test_data data = DT_TESTCASE(test_dt2, test_dt2_out);
|
||||
|
||||
root = test_common_deserialize(&data);
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
|
||||
// removing a property returns true if it existed
|
||||
TEST_ASSERT(dt_remove_prop(root, "prop"));
|
||||
// and false if not
|
||||
TEST_ASSERT(!dt_remove_prop(root, "prop"));
|
||||
TEST_ASSERT(!dt_remove_prop(root, "prop2"));
|
||||
|
||||
TEST_ASSERT(dt_find_node(root, "child1", &node));
|
||||
|
||||
dt_set_prop_32(node, "newprop32", 0x1234abcd);
|
||||
dt_set_prop_64(node, "newprop64", 0x1234567890abcdefULL);
|
||||
dt_set_prop_addr(node, "newpropaddr", 0x1122334455667788ULL);
|
||||
dt_set_prop_str(node, "newpropstr", "strprop");
|
||||
dt_set_prop(node, "newpropempty", "", 0);
|
||||
dt_set_prop(node, "newpropdata", "somedata\0xy", 11);
|
||||
|
||||
// After changing the node's name, it should be findable under
|
||||
// the new name, and not under the old one
|
||||
dt_set_prop_str(node, "name", "renamed-child");
|
||||
TEST_ASSERT(!dt_find_node(root, "child1", &node));
|
||||
TEST_ASSERT(dt_find_node(root, "renamed-child", &node));
|
||||
|
||||
// After changing the property's name, it should be findable under
|
||||
// the new name, and not under the old one
|
||||
prop_name = "prop";
|
||||
TEST_ASSERT(dt_get_prop(node, &prop_name, NULL, NULL));
|
||||
TEST_ASSERT(dt_rename_prop(node, "prop", "renamed-prop"));
|
||||
prop_name = "prop";
|
||||
TEST_ASSERT(!dt_get_prop(node, &prop_name, NULL, NULL));
|
||||
prop_name = "renamed-prop";
|
||||
TEST_ASSERT(dt_get_prop(node, &prop_name, NULL, NULL));
|
||||
|
||||
test_common_serialize(&data);
|
||||
}
|
||||
|
||||
uint8_t test_dt_update_shorter_in[] =
|
||||
// root node
|
||||
"\x04\x00\x00\x00" // 4 properties
|
||||
"\x00\x00\x00\x00" // 0 children
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 2nd property of root
|
||||
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x08\x00\x00\x00" // property length 8
|
||||
"12345678"
|
||||
|
||||
// 3rd property of root
|
||||
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x07\x00\x00\x00" // property length 7
|
||||
"1234567\0"
|
||||
|
||||
// 4th property of root
|
||||
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x09\x00\x00\x00" // property length 9
|
||||
"123456789\0\0\0"
|
||||
|
||||
// 5th property of root
|
||||
"prop4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x08\x00\x00\x00" // property length 8
|
||||
"12345678";
|
||||
|
||||
uint8_t test_dt_update_shorter_out[] =
|
||||
// root node
|
||||
"\x05\x00\x00\x00" // 5 properties
|
||||
"\x00\x00\x00\x00" // 0 children
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 2nd property of root
|
||||
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"abcd"
|
||||
|
||||
// 3rd property of root
|
||||
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"ABCD"
|
||||
|
||||
// 4th property of root
|
||||
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"QWER"
|
||||
|
||||
// 5th property of root
|
||||
"prop4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"ASDFG\0\0\0";
|
||||
|
||||
void test_dt_update_shorter(uintptr_t context)
|
||||
{
|
||||
dt_node_t *root;
|
||||
|
||||
dt_test_data data = DT_TESTCASE(test_dt_update_shorter_in, test_dt_update_shorter_out);
|
||||
|
||||
root = test_common_deserialize(&data);
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
|
||||
dt_set_prop(root, "prop1", "abcd", 4);
|
||||
dt_set_prop(root, "prop2", "ABCD", 4);
|
||||
dt_set_prop(root, "prop3", "QWER", 4);
|
||||
dt_set_prop(root, "prop4", "ASDFG", 5);
|
||||
|
||||
test_common_serialize(&data);
|
||||
}
|
||||
|
||||
uint8_t test_dt_placeholders_data[] =
|
||||
// root node
|
||||
"\x09\x00\x00\x00" // num properties
|
||||
"\x00\x00\x00\x00" // num children
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 2nd property of root (valid keys should get filled in)
|
||||
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0c\x00\x00\x80" // placeholder property length 12
|
||||
"syscfg/ABCD\0"
|
||||
|
||||
// 3rd property of root (invalid keys should get elided)
|
||||
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0c\x00\x00\x80" // placeholder property length 12
|
||||
"syscfg/NNNN\0"
|
||||
|
||||
// 4th property of root (valid key after invalid key should get filled in)
|
||||
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x18\x00\x00\x80" // placeholder property length 24
|
||||
"syscfg/NNNN,syscfg/ABCD\0"
|
||||
|
||||
// 5th property of root (1st value found should get copied)
|
||||
"prop4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x18\x00\x00\x80" // placeholder property length 24
|
||||
"syscfg/FFFF,syscfg/ABCD\0"
|
||||
|
||||
// 6th property of root (syscg value longer than placeholder)
|
||||
"prop5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0c\x00\x00\x80" // placeholder property length 12
|
||||
"syscfg/BigE\0"
|
||||
|
||||
// 7th property of root (syscg value truncated)
|
||||
"prop6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0e\x00\x00\x80" // placeholder property length 14
|
||||
"syscfg/BigE/6\0\0\0"
|
||||
|
||||
// 8th property of root (syscg value padded)
|
||||
"prop7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0f\x00\x00\x80" // placeholder property length 15
|
||||
"syscfg/ABCD/12\0\0"
|
||||
|
||||
// 9th property of root (macaddr from environment)
|
||||
"prop8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x10\x00\x00\x80" // placeholder property length 16
|
||||
"macaddr/ethaddr\0";
|
||||
|
||||
uint8_t test_dt_placeholders_out[] =
|
||||
// root node
|
||||
"\x08\x00\x00\x00" // num properties
|
||||
"\x00\x00\x00\x00" // num children
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 2nd property of root (valid keys should get filled in)
|
||||
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x06\x00\x00\x00" // property length 6
|
||||
"found!\0\0"
|
||||
|
||||
// 3rd property of root (valid key after invalid key should get filled in)
|
||||
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x06\x00\x00\x00" // property length 6
|
||||
"found!\0\0"
|
||||
|
||||
// 4th property of root (1st value found should get copied)
|
||||
"prop4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length 4
|
||||
"yess"
|
||||
|
||||
// 5th property of root (syscfg value longer than placeholder)
|
||||
"prop5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x1a\x00\x00\x00" // property length 26
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ\0\0"
|
||||
|
||||
// 6th property of root (syscfg value truncated)
|
||||
"prop6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x06\x00\x00\x00" // property length 6
|
||||
"ABCDEF\0\0"
|
||||
|
||||
// 7th property of root (syscfg value padded)
|
||||
"prop7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0c\x00\x00\x00" // property length 12
|
||||
"found!\0\0\0\0\0\0"
|
||||
|
||||
// 8th property of root (mac address)
|
||||
"prop8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x06\x00\x00\x00" // property length 6
|
||||
"\x01\x02\x03\x40\x50\x60\0\0";
|
||||
|
||||
void test_dt_placeholders(uintptr_t context)
|
||||
{
|
||||
dt_node_t *root;
|
||||
|
||||
mock_syscfg_reset();
|
||||
mock_syscfg_add('ABCD', "found!", strlen("found!"));
|
||||
mock_syscfg_add('FFFF', "yess", strlen("yess"));
|
||||
mock_syscfg_add('BigE', "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26);
|
||||
env_set("ethaddr", "01:02:03:40:50:60", 0);
|
||||
|
||||
dt_test_data data = DT_TESTCASE(test_dt_placeholders_data, test_dt_placeholders_out);
|
||||
|
||||
root = test_common_deserialize(&data);
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
|
||||
test_common_serialize(&data);
|
||||
}
|
||||
|
||||
uint8_t test_dt_placeholders_zeroes_data[] =
|
||||
// root node
|
||||
"\x04\x00\x00\x00" // num properties
|
||||
"\x00\x00\x00\x00" // num children
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 2nd property of root
|
||||
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x09\x00\x00\x80" // property length
|
||||
"zeroes/2\0\0\0\0"
|
||||
|
||||
// 3nd property of root
|
||||
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x09\x00\x00\x80" // property length
|
||||
"zeroes/4\0\0\0\0"
|
||||
|
||||
// 4th property of root
|
||||
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x0c\x00\x00\x80" // property length
|
||||
"zeroes/0x20\0";
|
||||
|
||||
|
||||
uint8_t test_dt_placeholders_zeroes_out[] =
|
||||
// root node
|
||||
"\x04\x00\x00\x00" // num properties
|
||||
"\x00\x00\x00\x00" // num children
|
||||
|
||||
// 1st property of root
|
||||
"name\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x05\x00\x00\x00" // property length 5
|
||||
"root\0\0\0\0"
|
||||
|
||||
// 2nd property of root
|
||||
"prop1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x02\x00\x00\x00" // property length
|
||||
"\0\0\0\0"
|
||||
|
||||
// 3nd property of root
|
||||
"prop2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x04\x00\x00\x00" // property length
|
||||
"\0\0\0\0"
|
||||
|
||||
// 4th property of root
|
||||
"prop3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\x20\x00\x00\x00" // property length
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
|
||||
|
||||
void test_dt_placeholders_zeroes(uintptr_t context)
|
||||
{
|
||||
dt_node_t *root;
|
||||
|
||||
dt_test_data data = DT_TESTCASE(test_dt_placeholders_zeroes_data, test_dt_placeholders_zeroes_out);
|
||||
|
||||
root = test_common_deserialize(&data);
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
|
||||
test_common_serialize(&data);
|
||||
}
|
||||
|
||||
void test_common_short(dt_test_data *data)
|
||||
{
|
||||
dt_node_t *root;
|
||||
|
||||
// Start by making sure the data is well-formed
|
||||
root = test_common_deserialize(data);
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
test_common_serialize(data);
|
||||
|
||||
// Now chop off a byte at a time, verifying we get a failure each time
|
||||
for (size_t i = 0; i < data->input_size; i++) {
|
||||
dt_init();
|
||||
|
||||
root = dt_deserialize((void *)data->input, i);
|
||||
TEST_ASSERT_NULL(root);
|
||||
}
|
||||
}
|
||||
|
||||
void test_dt_short1(uintptr_t context)
|
||||
{
|
||||
uint8_t input_data[] =
|
||||
"\x00\x00\x00\x00\00\x00\x00\x00" // no properties, no children
|
||||
;
|
||||
|
||||
dt_test_data data = DT_TESTCASE(input_data, input_data);
|
||||
|
||||
test_common_short(&data);
|
||||
}
|
||||
|
||||
void test_dt_short2(uintptr_t context)
|
||||
{
|
||||
uint8_t input_data[] =
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x00" // one property, no children
|
||||
"abcdefghijklmnopqrstuvqxzy01234\0" // 31-char name
|
||||
"\x03\x00\x00\x00" // property len 3
|
||||
"abc\0" // property value
|
||||
;
|
||||
|
||||
dt_test_data data = DT_TESTCASE(input_data, input_data);
|
||||
|
||||
test_common_short(&data);
|
||||
}
|
||||
|
||||
void test_dt_short3(uintptr_t context)
|
||||
{
|
||||
uint8_t input_data[] =
|
||||
"\x01\x00\x00\x00\x01\x00\x00\x00" // one property, one child
|
||||
"abcdefghijklmnopqrstuvqxzy01234\0" // 31-char name
|
||||
"\x03\x00\x00\x00" // property len 3
|
||||
"abc\0" // property value
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" // no properties, no children
|
||||
;
|
||||
|
||||
dt_test_data data = DT_TESTCASE(input_data, input_data);
|
||||
|
||||
test_common_short(&data);
|
||||
}
|
||||
|
||||
void test_dt_bad_name(uintptr_t context)
|
||||
{
|
||||
uint8_t input_data[] =
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x00" // one property, no children
|
||||
"abcdefghijklmnopqrstuvqxzy012345" // 32-char name
|
||||
"\x03\x00\x00\x00" // property len 3
|
||||
"abc\0" // property value
|
||||
;
|
||||
dt_node_t *root;
|
||||
|
||||
dt_test_data data = DT_TESTCASE_FAIL(input_data);
|
||||
|
||||
dt_init();
|
||||
root = dt_deserialize(data.input, data.input_size);
|
||||
TEST_ASSERT_NULL(root);
|
||||
}
|
||||
|
||||
static struct test_suite devicetree_test_suite = {
|
||||
.name = "devicetree",
|
||||
.description = "tests the devicetree parser",
|
||||
.test_cases = {
|
||||
{ "devicetree_legacy", test_devicetree_legacy, 0 },
|
||||
{ "dt_deserialize", test_dt_deserialize, 0 },
|
||||
{ "dt_serialize", test_dt_serialize, 0 },
|
||||
{ "dt_serialize_too_small0", test_dt_serialize, 0 },
|
||||
{ "dt_serialize_too_small1", test_dt_serialize, 1 },
|
||||
{ "dt_serialize_too_small-1", test_dt_serialize, (uintptr_t)-1LL },
|
||||
{ "dt_update", test_dt_update, 0 },
|
||||
{ "dt_update_shorter", test_dt_update_shorter, 0 },
|
||||
{ "dt_placeholders", test_dt_placeholders, 0 },
|
||||
{ "dt_placeholders_zeroes", test_dt_placeholders_zeroes, 0 },
|
||||
{ "dt_short1", test_dt_short1, 0 },
|
||||
{ "dt_short2", test_dt_short2, 0 },
|
||||
{ "dt_short3", test_dt_short3, 0 },
|
||||
{ "dt_bad_name", test_dt_bad_name, 0 },
|
||||
TEST_CASE_LAST
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE(devicetree_test_suite);
|
||||
|
||||
bool env_blacklist(const char *name, bool write)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool env_blacklist_nvram(const char *name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := devicetree
|
||||
|
||||
TEST_OBJS := \
|
||||
$(LOCAL_DIR)/devicetree.o \
|
||||
lib/env/env.o
|
||||
|
||||
TEST_SUPPORT_OBJS := \
|
||||
tests/unittest-main.o \
|
||||
tests/mocks/syscfg.o \
|
||||
$(LOCAL_DIR)/tests.o
|
||||
@@ -0,0 +1,9 @@
|
||||
Table of Contents:
|
||||
|
||||
README - this file, containing an overview of the contents of the AppleEffaceableStorage project
|
||||
AppleEffaceableStorage.xcodeproj - xcode project definition bundle for building iokit driver and ios utility command
|
||||
common - portable core of the effaceable storage implementation, common to all build contexts
|
||||
docs - various notes and (coming soon) design documents discussing effaceable storage
|
||||
iboot - code specific to hosting portable core in iBoot context
|
||||
iokit - code specific to hosting portable core in IOKit context
|
||||
ios - code providing 'effaceable' command line utility in iOS; used only for user client example and internal development
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_CONTEXT_H_
|
||||
#define _EFFACEABLE_CONTEXT_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
struct _effaceable_nand_context
|
||||
{
|
||||
void * buf;
|
||||
|
||||
effaceable_storage_t * storage;
|
||||
effaceable_system_t * system;
|
||||
effaceable_nand_hal_t * nand;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
struct _effaceable_nor_context
|
||||
{
|
||||
effaceable_storage_t * storage;
|
||||
effaceable_system_t * system;
|
||||
effaceable_nor_hal_t * nor;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
struct _effaceable_storage_context
|
||||
{
|
||||
bool enable_full_scan;
|
||||
bool enable_wipe;
|
||||
bool is_formatted;
|
||||
uint32_t generation;
|
||||
uint32_t group;
|
||||
void * cache;
|
||||
void * scratch;
|
||||
void * sniff;
|
||||
void * crypt;
|
||||
void * nonce_hash;
|
||||
|
||||
locker_list_t locker_list;
|
||||
|
||||
effaceable_system_t * system;
|
||||
effaceable_device_t * device;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
typedef struct _effaceable_nor_context effaceable_nor_context_t;
|
||||
typedef struct _effaceable_nand_context effaceable_nand_context_t;
|
||||
typedef struct _effaceable_storage_context effaceable_storage_context_t;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_CONTEXT_H_ */
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_DELEGATION_H_
|
||||
#define _EFFACEABLE_DELEGATION_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define isFnAvailable(_me, _it, _fn) (0 != context(_me)->_it->_fn)
|
||||
#define delegateFn(_me, _it, _fn, ...) context(_me)->_it->_fn(context(_me)->_it, ## __VA_ARGS__)
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_DELEGATION_H_ */
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _EFFACEABLE_LOCKER_H_
|
||||
#define _EFFACEABLE_LOCKER_H_
|
||||
|
||||
#include "queue.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
struct _locker;
|
||||
struct _locker_list;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct _locker locker_t;
|
||||
typedef TAILQ_HEAD(_locker_list, _locker) locker_list_t;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct _locker
|
||||
{
|
||||
TAILQ_ENTRY(_locker) link;
|
||||
AppleEffaceableStorageLockerHeader header;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_LOCKER_H_ */
|
||||
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* COPYRIGHT (c) 2010-11 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 <effaceable_contract.h>
|
||||
#include <effaceable_debug.h>
|
||||
|
||||
#include "effaceable_private_types.h"
|
||||
#include "effaceable_storage_dev.h"
|
||||
#include "effaceable_nand_core.h"
|
||||
#include "effaceable_delegation.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define debug(...) dlogf(device, __VA_ARGS__ )
|
||||
|
||||
// =============================================================================
|
||||
|
||||
extern void
|
||||
startEffaceableNAND(effaceable_device_t * device, effaceable_system_t * system, effaceable_nand_hal_t * nand, effaceable_storage_t * storage)
|
||||
{
|
||||
initContext(device, system, nand, storage);
|
||||
initContract(device);
|
||||
|
||||
setBuf(device, allocMem(device, getUnitSize(device)));
|
||||
|
||||
startEffaceableStorage(storage, device, system);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t
|
||||
getGroupCount(effaceable_device_t * device)
|
||||
{
|
||||
return ((uint32_t)(getBankCount(device) * getBlockCount(device)));
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
getUnitsPerGroup(effaceable_device_t * device)
|
||||
{
|
||||
return ((uint32_t)(getPageCount(device) - kAppleEffaceableNANDTotalMetaPages));
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
getUnitSize(effaceable_device_t * device)
|
||||
{
|
||||
return getPageSize(device);
|
||||
}
|
||||
|
||||
static EffaceableReturn
|
||||
eraseGroup(effaceable_device_t * device, uint32_t group)
|
||||
{
|
||||
const uint32_t bank = mapGroupToBank(device, group);
|
||||
const uint32_t block = mapGroupToBlock(device, group);
|
||||
|
||||
EffaceableReturn ok = kEffaceableReturnError;
|
||||
|
||||
if (isBlockBad(device, bank, block)) {
|
||||
// XXX remap block
|
||||
ok = kEffaceableReturnBadMedia;
|
||||
} else if (kEffaceableReturnSuccess == (ok = eraseBlock(device, bank, block))) {
|
||||
openBlock(device, bank, block);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void
|
||||
openBlock(effaceable_device_t * device, uint32_t bank, uint32_t block)
|
||||
{
|
||||
uint32_t page;
|
||||
const uint32_t start = 0;
|
||||
const uint32_t after = kAppleEffaceableNANDOpeningMetaPages;
|
||||
void * buf = 0;
|
||||
|
||||
buf = getBuf(device);
|
||||
prepOpenCloseMeta(device, kAppleEffaceableNANDMetaOpenMagic, buf);
|
||||
for (page = start; page < after; page++) {
|
||||
writePage(device, bank, block, page, buf, getPageSize(device));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
closeBlock(effaceable_device_t * device, uint32_t bank, uint32_t block)
|
||||
{
|
||||
uint32_t page;
|
||||
const uint32_t start = (getPageCount(device) - kAppleEffaceableNANDClosingMetaPages);
|
||||
const uint32_t after = getPageCount(device);
|
||||
void * buf = 0;
|
||||
|
||||
buf = getBuf(device);
|
||||
prepOpenCloseMeta(device, kAppleEffaceableNANDMetaCloseMagic, buf);
|
||||
prepCloseContent(device, buf);
|
||||
for (page = start; page < after; page++) {
|
||||
writePage(device, bank, block, page, buf, getPageSize(device));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
prepOpenCloseMeta(effaceable_device_t * device, uint32_t magic, void * buf)
|
||||
{
|
||||
AppleEffaceableNANDMeta * meta = (AppleEffaceableNANDMeta*)buf;
|
||||
|
||||
whitenMeta(device, meta);
|
||||
meta->header.magic = magic;
|
||||
meta->header.version_major = kAppleEffaceableNANDMetaVersionMajorCurrent;
|
||||
meta->header.version_minor = kAppleEffaceableNANDMetaVersionMinorCurrent;
|
||||
meta->header.flags = kAppleEffaceableNANDMetaFlagsNone;
|
||||
}
|
||||
|
||||
static void
|
||||
prepCloseContent(effaceable_device_t * device, void * buf)
|
||||
{
|
||||
AppleEffaceableNANDMeta * meta = (AppleEffaceableNANDMeta*)buf;
|
||||
|
||||
if (requestPartitionTableDiff(device, meta->content, getMetaContentSize(device))) {
|
||||
meta->header.flags |= kAppleEffaceableNANDMetaFlagsHasPTabDiff;
|
||||
debug(INFO, "successfully requested PTabDiff");
|
||||
} else {
|
||||
debug(WARN, "unable to request PTabDiff");
|
||||
}
|
||||
}
|
||||
|
||||
static EffaceableReturn
|
||||
writeUnit(effaceable_device_t * device, const void * buf, uint32_t group, uint32_t unit)
|
||||
{
|
||||
const uint32_t bank = mapGroupToBank(device, group);
|
||||
const uint32_t block = mapGroupToBlock(device, group);
|
||||
const uint32_t page = mapUnitToPage(device, unit);
|
||||
|
||||
EffaceableReturn ok = kEffaceableReturnError;
|
||||
|
||||
if (isBlockBad(device, bank, block)) {
|
||||
// XXX replace block iff passing below critical good block count threshold
|
||||
ok = kEffaceableReturnBadMedia;
|
||||
} else {
|
||||
if (kEffaceableReturnSuccess == (ok = writePage(device, bank, block, page, buf, getPageSize(device)))) {
|
||||
// if that was final content page, write block closing meta pages
|
||||
if ((getUnitsPerGroup(device) - 1) == unit) {
|
||||
closeBlock(device, bank, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static EffaceableReturn
|
||||
readUnit(effaceable_device_t * device, void * buf, uint32_t group, uint32_t unit)
|
||||
{
|
||||
const uint32_t bank = mapGroupToBank(device, group);
|
||||
const uint32_t block = mapGroupToBlock(device, group);
|
||||
const uint32_t page = mapUnitToPage(device, unit);
|
||||
|
||||
EffaceableReturn ok;
|
||||
|
||||
if (isBlockBad(device, bank, block)) {
|
||||
// XXX replace block iff passing below critical good block count threshold
|
||||
ok = kEffaceableReturnBadMedia;
|
||||
} else {
|
||||
ok = readPage(device, bank, block, page, buf, getPageSize(device));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
mapGroupToBank(effaceable_device_t * device, uint32_t group)
|
||||
{
|
||||
const uint32_t bank = ((uint32_t)group) % getBankCount(device);
|
||||
// XXX check bounds
|
||||
return bank;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
mapGroupToBlock(effaceable_device_t * device, uint32_t group)
|
||||
{
|
||||
const uint32_t block = ((uint32_t)group) / getBankCount(device);
|
||||
// XXX check bounds
|
||||
return block;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
mapUnitToPage(effaceable_device_t * device, uint32_t unit)
|
||||
{
|
||||
const uint32_t page = ((uint32_t)unit) + kAppleEffaceableNANDOpeningMetaPages;
|
||||
// XXX check bounds
|
||||
return page;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static uint32_t
|
||||
getCopiesSize(effaceable_device_t * device)
|
||||
{
|
||||
return getCopySize(device) * getCopiesPerUnit(device);
|
||||
}
|
||||
|
||||
static void
|
||||
cleanBuffers(effaceable_device_t * device)
|
||||
{
|
||||
setMem(device, getBuf(device), 0, getCopiesSize(device));
|
||||
}
|
||||
|
||||
static void
|
||||
whitenExtra(effaceable_device_t * device, void * buf)
|
||||
{
|
||||
// XXX seems like this should be getting called; why isn't it?
|
||||
const uint32_t copies_size = getCopiesSize(device);
|
||||
const uint32_t extra_size = getUnitSize(device) - copies_size;
|
||||
readRandom(device, ((uint8_t*)buf) + copies_size, extra_size);
|
||||
}
|
||||
|
||||
static void
|
||||
whitenMeta(effaceable_device_t * device, void * buf)
|
||||
{
|
||||
readRandom(device, buf, getPageSize(device));
|
||||
}
|
||||
|
||||
static void
|
||||
foundCurrentCloneInGroup(effaceable_device_t * device, uint32_t group)
|
||||
{
|
||||
const uint32_t magic = kAppleEffaceableNANDMetaCloseMagic;
|
||||
const uint32_t major = kAppleEffaceableNANDMetaVersionMajorPTabDiff;
|
||||
const uint32_t minor = kAppleEffaceableNANDMetaVersionMinorPTabDiff;
|
||||
const uint32_t flag = kAppleEffaceableNANDMetaFlagsHasPTabDiff;
|
||||
|
||||
const uint32_t bank = mapGroupToBank(device, group);
|
||||
const uint32_t block = mapGroupToBlock(device, group);
|
||||
const uint32_t start = (getPageCount(device) - kAppleEffaceableNANDClosingMetaPages);
|
||||
const uint32_t after = getPageCount(device);
|
||||
|
||||
void * buf = getBuf(device);
|
||||
|
||||
uint32_t page;
|
||||
AppleEffaceableNANDMeta * meta;
|
||||
|
||||
for (page = start; page < after; page++) {
|
||||
debug(INFO, "scanning %lu:%lu:%lu for ptab diff", bank, block, page);
|
||||
if (kEffaceableReturnSuccess == readPage(device, bank, block, page, buf, getPageSize(device))) {
|
||||
|
||||
meta = (AppleEffaceableNANDMeta *)buf;
|
||||
if (magic != meta->header.magic) {
|
||||
debug(ERR, "bad magic for closing page");
|
||||
} else if (0 > compareMetaVersions(device, major, minor, meta->header.version_major, meta->header.version_minor)) {
|
||||
debug(INFO, "meta version precedes ptab diff implementation");
|
||||
} else if (flag != (flag & meta->header.flags)) {
|
||||
debug(INFO, "meta doesn't have ptab diff content");
|
||||
} else if (!providePartitionTableDiff(device, meta->content, getMetaContentSize(device))) {
|
||||
debug(WARN, "ptab diff content rejected");
|
||||
} else {
|
||||
debug(INFO, "successfully provided ptab diff");
|
||||
}
|
||||
|
||||
/* readable closing pages should all be identical; no reason to continue */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
compareMetaVersions(effaceable_device_t * device, uint32_t major1, uint32_t minor1, uint32_t major2, uint32_t minor2)
|
||||
{
|
||||
if (major1 < major2) {
|
||||
return -1;
|
||||
} else if (major1 > major2) {
|
||||
return 1;
|
||||
} else if (minor1 < minor2) {
|
||||
return -1;
|
||||
} else if (minor1 > minor2) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
getMetaContentSize(effaceable_device_t * device)
|
||||
{
|
||||
return (getPageSize(device) - kAppleEffaceableNANDMetaHeaderSize);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
initContract(effaceable_device_t * device)
|
||||
{
|
||||
// hook required operations
|
||||
device->getGroupCount = getGroupCount;
|
||||
device->getUnitsPerGroup = getUnitsPerGroup;
|
||||
device->getUnitSize = getUnitSize;
|
||||
device->eraseGroup = eraseGroup;
|
||||
device->writeUnit = writeUnit;
|
||||
device->readUnit = readUnit;
|
||||
|
||||
// hook optional operations
|
||||
device->cleanBuffers = cleanBuffers;
|
||||
device->foundCurrentCloneInGroup = foundCurrentCloneInGroup;
|
||||
device->updateDynamicProperties = (void *)0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static inline effaceable_nand_context_t * context(effaceable_device_t * device)
|
||||
{
|
||||
return (effaceable_nand_context_t *)device->opaque;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline void * getBuf(effaceable_device_t * device)
|
||||
{
|
||||
return context(device)->buf;
|
||||
}
|
||||
|
||||
static inline void * setBuf(effaceable_device_t * device, void * buf)
|
||||
{
|
||||
return context(device)->buf = buf;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getCopiesPerUnit(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, storage, getCopiesPerUnit);
|
||||
}
|
||||
|
||||
static inline uint32_t getCopySize(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, storage, getCopySize);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getPageSize(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, nand, getPageSize);
|
||||
}
|
||||
|
||||
static inline uint32_t getPageCount(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, nand, getPageCount);
|
||||
}
|
||||
|
||||
static inline uint32_t getBlockCount(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, nand, getBlockCount);
|
||||
}
|
||||
|
||||
static inline uint32_t getBankCount(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, nand, getBankCount);
|
||||
}
|
||||
|
||||
static inline bool isBlockBad(effaceable_device_t * device, uint32_t bank, uint32_t block)
|
||||
{
|
||||
return delegateFn(device, nand, isBlockBad, bank, block);
|
||||
}
|
||||
|
||||
static inline EffaceableReturn eraseBlock(effaceable_device_t * device, uint32_t bank, uint32_t block)
|
||||
{
|
||||
return delegateFn(device, nand, eraseBlock, bank, block);
|
||||
}
|
||||
|
||||
static inline EffaceableReturn writePage(effaceable_device_t * device, uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length)
|
||||
{
|
||||
return delegateFn(device, nand, writePage, bank, block, page, buf, length);
|
||||
}
|
||||
|
||||
static inline EffaceableReturn readPage(effaceable_device_t * device, uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length)
|
||||
{
|
||||
return delegateFn(device, nand, readPage, bank, block, page, buf, length);
|
||||
}
|
||||
|
||||
static inline bool requestPartitionTableDiff(effaceable_device_t * device, void * buf, uint32_t length)
|
||||
{
|
||||
return delegateFn(device, nand, requestPartitionTableDiff, buf, length);
|
||||
}
|
||||
|
||||
static inline bool providePartitionTableDiff(effaceable_device_t * device, void * buf, uint32_t length)
|
||||
{
|
||||
return delegateFn(device, nand, providePartitionTableDiff, buf, length);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline void * allocMem(effaceable_device_t * device, uint32_t size)
|
||||
{
|
||||
return delegateFn(device, system, allocMem, size);
|
||||
}
|
||||
|
||||
static inline void freeMem(effaceable_device_t * device, void * buf, uint32_t size)
|
||||
{
|
||||
delegateFn(device, system, freeMem, buf, size);
|
||||
}
|
||||
|
||||
static inline void * setMem(effaceable_device_t * device, void * buf, uint8_t val, uint32_t size)
|
||||
{
|
||||
return delegateFn(device, system, setMem, buf, val, size);
|
||||
}
|
||||
|
||||
static inline void * moveMem(effaceable_device_t * device, void * dst, const void * src, uint32_t size)
|
||||
{
|
||||
return delegateFn(device, system, moveMem, dst, src, size);
|
||||
}
|
||||
|
||||
static inline int cmpMem(effaceable_device_t * device, const void * lhs, const void * rhs, uint32_t size)
|
||||
{
|
||||
return delegateFn(device, system, cmpMem, lhs, rhs, size);
|
||||
}
|
||||
|
||||
static inline void readRandom(effaceable_device_t * device, void * buf, uint32_t size)
|
||||
{
|
||||
delegateFn(device, system, readRandom, buf, size);
|
||||
}
|
||||
|
||||
static inline uint32_t crc32Sys(effaceable_device_t * device, uint32_t crc, const void * buf, uint32_t size, bool finish)
|
||||
{
|
||||
return delegateFn(device, system, crc32, crc, buf, size, finish);
|
||||
}
|
||||
|
||||
static inline bool setPropertySys(effaceable_device_t * device, const char * key, uint32_t value)
|
||||
{
|
||||
return delegateFn(device, system, setProperty, key, value);
|
||||
}
|
||||
|
||||
static inline void panicSys(effaceable_device_t * device, const char * msg)
|
||||
{
|
||||
delegateFn(device, system, panicSys, msg);
|
||||
}
|
||||
|
||||
static int logf(effaceable_device_t * device, const char * fmt, ...)
|
||||
{
|
||||
int err;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
err = delegateFn(device, system, vlogf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
initContext(effaceable_device_t * device, effaceable_system_t * system, effaceable_nand_hal_t * nand, effaceable_storage_t * storage)
|
||||
{
|
||||
device->opaque = system->allocMem(system, sizeof(effaceable_nand_context_t));
|
||||
system->setMem(system, context(device), 0, sizeof(effaceable_nand_context_t));
|
||||
|
||||
context(device)->storage = storage;
|
||||
context(device)->system = system;
|
||||
context(device)->nand = nand;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _EFFACEABLE_NAND_CORE_H_
|
||||
#define _EFFACEABLE_NAND_CORE_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// reserve pages at beginning and end of block for future metadata
|
||||
#define kAppleEffaceableNANDOpeningMetaPages 16
|
||||
#define kAppleEffaceableNANDClosingMetaPages 16
|
||||
#define kAppleEffaceableNANDTotalMetaPages (kAppleEffaceableNANDOpeningMetaPages + kAppleEffaceableNANDClosingMetaPages)
|
||||
|
||||
#define kAppleEffaceableNANDMetaOpenMagic 'open'
|
||||
#define kAppleEffaceableNANDMetaCloseMagic 'clos'
|
||||
|
||||
#define kAppleEffaceableNANDMetaVersionMajorUnknown ((uint32_t)0x00000000)
|
||||
#define kAppleEffaceableNANDMetaVersionMajorInitial ((uint32_t)0x00000001)
|
||||
#define kAppleEffaceableNANDMetaVersionMajorBungled ((uint32_t)0x00000002)
|
||||
#define kAppleEffaceableNANDMetaVersionMajorPTabDiff ((uint32_t)0x00000002)
|
||||
#define kAppleEffaceableNANDMetaVersionMajorCurrent kAppleEffaceableNANDMetaVersionMajorPTabDiff
|
||||
#define kAppleEffaceableNANDMetaVersionMajorInvalid ((uint32_t)0xFFFFFFFF)
|
||||
|
||||
#define kAppleEffaceableNANDMetaVersionMinorUnknown ((uint32_t)0x00000000)
|
||||
#define kAppleEffaceableNANDMetaVersionMinorInitial ((uint32_t)0x00000001)
|
||||
#define kAppleEffaceableNANDMetaVersionMinorBungled ((uint32_t)0x00000002)
|
||||
#define kAppleEffaceableNANDMetaVersionMinorPTabDiff ((uint32_t)0x00000003)
|
||||
#define kAppleEffaceableNANDMetaVersionMinorCurrent kAppleEffaceableNANDMetaVersionMinorPTabDiff
|
||||
#define kAppleEffaceableNANDMetaVersionMinorInvalid ((uint32_t)0xFFFFFFFF)
|
||||
|
||||
#define kAppleEffaceableNANDMetaFlagsNone ((uint32_t)0x00000000)
|
||||
#define kAppleEffaceableNANDMetaFlagsHasPTabDiff ((uint32_t)0x00000001)
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// All structures defined here are on-media formats.
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic;
|
||||
uint32_t version_major;
|
||||
uint32_t version_minor;
|
||||
uint32_t flags;
|
||||
|
||||
} __attribute__((__packed__)) AppleEffaceableNANDMetaHeader;
|
||||
|
||||
#define kAppleEffaceableNANDMetaHeaderSize sizeof(AppleEffaceableNANDMetaHeader)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AppleEffaceableNANDMetaHeader header;
|
||||
uint8_t content[0];
|
||||
|
||||
} __attribute__((__packed__)) AppleEffaceableNANDMeta;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t getGroupCount(effaceable_device_t * device);
|
||||
static uint32_t getUnitsPerGroup(effaceable_device_t * device);
|
||||
static uint32_t getUnitSize(effaceable_device_t * device);
|
||||
static EffaceableReturn eraseGroup(effaceable_device_t * device, uint32_t group);
|
||||
static EffaceableReturn writeUnit(effaceable_device_t * device, const void * buf, uint32_t group, uint32_t unit);
|
||||
static EffaceableReturn readUnit(effaceable_device_t * device, void * buf, uint32_t group, uint32_t unit);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void openBlock(effaceable_device_t * device, uint32_t bank, uint32_t block);
|
||||
static void closeBlock(effaceable_device_t * device, uint32_t bank, uint32_t block);
|
||||
static void prepOpenCloseMeta(effaceable_device_t * device, uint32_t magic, void * buf);
|
||||
static void prepCloseContent(effaceable_device_t * device, void * buf);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t mapGroupToBank(effaceable_device_t * device, uint32_t group);
|
||||
static uint32_t mapGroupToBlock(effaceable_device_t * device, uint32_t group);
|
||||
static uint32_t mapUnitToPage(effaceable_device_t * device, uint32_t unit);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t getCopiesSize(effaceable_device_t * device);
|
||||
static void cleanBuffers(effaceable_device_t * device);
|
||||
static void whitenExtra(effaceable_device_t * device, void * buf);
|
||||
static void whitenMeta(effaceable_device_t * device, void * buf);
|
||||
static void foundCurrentCloneInGroup(effaceable_device_t * device, uint32_t group);
|
||||
static int compareMetaVersions(effaceable_device_t * device, uint32_t major1, uint32_t minor1, uint32_t major2, uint32_t minor2);
|
||||
static uint32_t getMetaContentSize(effaceable_device_t * device);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void initContract(effaceable_device_t * device);
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static inline void * getBuf(effaceable_device_t * device);
|
||||
static inline void * setBuf(effaceable_device_t * device, void * buf);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getCopiesPerUnit(effaceable_device_t * device);
|
||||
static inline uint32_t getCopySize(effaceable_device_t * device);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getPageSize(effaceable_device_t * device);
|
||||
static inline uint32_t getPageCount(effaceable_device_t * device);
|
||||
static inline uint32_t getBlockCount(effaceable_device_t * device);
|
||||
static inline uint32_t getBankCount(effaceable_device_t * device);
|
||||
static inline bool isBlockBad(effaceable_device_t * device, uint32_t bank, uint32_t block);
|
||||
static inline EffaceableReturn eraseBlock(effaceable_device_t * device, uint32_t bank, uint32_t block);
|
||||
static inline EffaceableReturn writePage(effaceable_device_t * device, uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length);
|
||||
static inline EffaceableReturn readPage(effaceable_device_t * device, uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length);
|
||||
static inline bool requestPartitionTableDiff(effaceable_device_t * device, void * buf, uint32_t length);
|
||||
static inline bool providePartitionTableDiff(effaceable_device_t * device, void * buf, uint32_t length);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline void * allocMem(effaceable_device_t * device, uint32_t size);
|
||||
static inline void freeMem(effaceable_device_t * device, void * buf, uint32_t size);
|
||||
static inline void * setMem(effaceable_device_t * device, void * buf, uint8_t val, uint32_t size);
|
||||
static inline void * moveMem(effaceable_device_t * device, void * dst, const void * src, uint32_t size);
|
||||
static inline int cmpMem(effaceable_device_t * device, const void * lhs, const void * rhs, uint32_t size);
|
||||
static inline void readRandom(effaceable_device_t * device, void * buf, uint32_t size);
|
||||
static inline uint32_t crc32Sys(effaceable_device_t * device, uint32_t crc, const void * buf, uint32_t size, bool finish);
|
||||
static inline bool setPropertySys(effaceable_device_t * device, const char * key, uint32_t value);
|
||||
static inline void panicSys(effaceable_device_t * device, const char * msg);
|
||||
|
||||
static int logf(effaceable_device_t * device, const char * fmt, ...);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void initContext(effaceable_device_t * device, effaceable_system_t * system, effaceable_nand_hal_t * nand, effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
__END_DECLS
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_NAND_CORE_H_ */
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 <effaceable_contract.h>
|
||||
#include <effaceable_debug.h>
|
||||
|
||||
#include "effaceable_private_types.h"
|
||||
#include "effaceable_storage_dev.h"
|
||||
#include "effaceable_nor_core.h"
|
||||
#include "effaceable_delegation.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define debug(...) dlogf(device, __VA_ARGS__ )
|
||||
|
||||
// =============================================================================
|
||||
|
||||
extern void
|
||||
startEffaceableNOR(effaceable_device_t * device, effaceable_system_t * system, effaceable_nor_hal_t * nor, effaceable_storage_t * storage)
|
||||
{
|
||||
initContext(device, system, nor, storage);
|
||||
initContract(device);
|
||||
|
||||
startEffaceableStorage(storage, device, system);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static uint32_t
|
||||
getGroupCount(effaceable_device_t * device)
|
||||
{
|
||||
return getRegionCount(device);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
getUnitsPerGroup(effaceable_device_t * device)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
getUnitSize(effaceable_device_t * device)
|
||||
{
|
||||
return getRegionSize(device, 0);
|
||||
}
|
||||
|
||||
static EffaceableReturn
|
||||
eraseGroup(effaceable_device_t * device, uint32_t group)
|
||||
{
|
||||
return eraseRegion(device, group, getUnitSize(device));
|
||||
}
|
||||
|
||||
static EffaceableReturn
|
||||
writeUnit(effaceable_device_t * device, const void * buf, uint32_t group, uint32_t unit)
|
||||
{
|
||||
// XXX assert unit == 0
|
||||
return writeRegion(device, group, buf, getUnitSize(device));
|
||||
}
|
||||
|
||||
static EffaceableReturn
|
||||
readUnit(effaceable_device_t * device, void * buf, uint32_t group, uint32_t unit)
|
||||
{
|
||||
// XXX assert unit == 0
|
||||
return readRegion(device, group, buf, getUnitSize(device));
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static void
|
||||
initContract(effaceable_device_t * device)
|
||||
{
|
||||
// hook required operations
|
||||
device->getGroupCount = getGroupCount;
|
||||
device->getUnitsPerGroup = getUnitsPerGroup;
|
||||
device->getUnitSize = getUnitSize;
|
||||
device->eraseGroup = eraseGroup;
|
||||
device->writeUnit = writeUnit;
|
||||
device->readUnit = readUnit;
|
||||
|
||||
// hook optional operations
|
||||
device->cleanBuffers = (void *)0;
|
||||
device->foundCurrentCloneInGroup = (void *)0;
|
||||
device->updateDynamicProperties = (void *)0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static inline effaceable_nor_context_t * context(effaceable_device_t * device)
|
||||
{
|
||||
return (effaceable_nor_context_t *)device->opaque;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getCopiesPerUnit(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, storage, getCopiesPerUnit);
|
||||
}
|
||||
|
||||
static inline uint32_t getCopySize(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, storage, getCopySize);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getRegionCount(effaceable_device_t * device)
|
||||
{
|
||||
return delegateFn(device, nor, getRegionCount);
|
||||
}
|
||||
|
||||
static inline uint32_t getRegionSize(effaceable_device_t * device, uint32_t index)
|
||||
{
|
||||
return delegateFn(device, nor, getRegionSize, index);
|
||||
}
|
||||
|
||||
static inline EffaceableReturn eraseRegion(effaceable_device_t * device, uint32_t index, uint32_t length)
|
||||
{
|
||||
return delegateFn(device, nor, eraseRegion, index, length);
|
||||
}
|
||||
|
||||
static inline uint32_t writeRegion(effaceable_device_t * device, uint32_t index, const void * buf, uint32_t length)
|
||||
{
|
||||
return delegateFn(device, nor, writeRegion, index, buf, length);
|
||||
}
|
||||
|
||||
static inline bool readRegion(effaceable_device_t * device, uint32_t index, void * buf, uint32_t length)
|
||||
{
|
||||
return delegateFn(device, nor, readRegion, index, buf, length);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline void * allocMem(effaceable_device_t * device, uint32_t size)
|
||||
{
|
||||
return delegateFn(device, system, allocMem, size);
|
||||
}
|
||||
|
||||
static inline void freeMem(effaceable_device_t * device, void * buf, uint32_t size)
|
||||
{
|
||||
delegateFn(device, system, freeMem, buf, size);
|
||||
}
|
||||
|
||||
static inline void * setMem(effaceable_device_t * device, void * buf, uint8_t val, uint32_t size)
|
||||
{
|
||||
return delegateFn(device, system, setMem, buf, val, size);
|
||||
}
|
||||
|
||||
static inline void * moveMem(effaceable_device_t * device, void * dst, const void * src, uint32_t size)
|
||||
{
|
||||
return delegateFn(device, system, moveMem, dst, src, size);
|
||||
}
|
||||
|
||||
static inline int cmpMem(effaceable_device_t * device, const void * lhs, const void * rhs, uint32_t size)
|
||||
{
|
||||
return delegateFn(device, system, cmpMem, lhs, rhs, size);
|
||||
}
|
||||
|
||||
static inline void readRandom(effaceable_device_t * device, void * buf, uint32_t size)
|
||||
{
|
||||
delegateFn(device, system, readRandom, buf, size);
|
||||
}
|
||||
|
||||
static inline uint32_t crc32Sys(effaceable_device_t * device, uint32_t crc, const void * buf, uint32_t size, bool finish)
|
||||
{
|
||||
return delegateFn(device, system, crc32, crc, buf, size, finish);
|
||||
}
|
||||
|
||||
static inline bool setPropertySys(effaceable_device_t * device, const char * key, uint32_t value)
|
||||
{
|
||||
return delegateFn(device, system, setProperty, key, value);
|
||||
}
|
||||
|
||||
static inline void panicSys(effaceable_device_t * device, const char * msg)
|
||||
{
|
||||
delegateFn(device, system, panicSys, msg);
|
||||
}
|
||||
|
||||
static int logf(effaceable_device_t * device, const char * fmt, ...)
|
||||
{
|
||||
int err;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
err = delegateFn(device, system, vlogf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static void
|
||||
initContext(effaceable_device_t * device, effaceable_system_t * system, effaceable_nor_hal_t * nor, effaceable_storage_t * storage)
|
||||
{
|
||||
device->opaque = system->allocMem(system, sizeof(effaceable_nor_context_t));
|
||||
system->setMem(system, context(device), 0, sizeof(effaceable_nor_context_t));
|
||||
|
||||
context(device)->system = system;
|
||||
context(device)->nor = nor;
|
||||
context(device)->storage = storage;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _EFFACEABLE_NOR_CORE_H_
|
||||
#define _EFFACEABLE_NOR_CORE_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t getGroupCount(effaceable_device_t * device);
|
||||
static uint32_t getUnitsPerGroup(effaceable_device_t * device);
|
||||
static uint32_t getUnitSize(effaceable_device_t * device);
|
||||
static EffaceableReturn eraseGroup(effaceable_device_t * device, uint32_t group);
|
||||
static EffaceableReturn writeUnit(effaceable_device_t * device, const void * buf, uint32_t group, uint32_t unit);
|
||||
static EffaceableReturn readUnit(effaceable_device_t * device, void * buf, uint32_t group, uint32_t unit);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void initContract(effaceable_device_t * device);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getCopiesPerUnit(effaceable_device_t * device);
|
||||
static inline uint32_t getCopySize(effaceable_device_t * device);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getRegionCount(effaceable_device_t * device);
|
||||
static inline uint32_t getRegionSize(effaceable_device_t * device, uint32_t index);
|
||||
static inline EffaceableReturn eraseRegion(effaceable_device_t * device, uint32_t index, uint32_t length);
|
||||
static inline uint32_t writeRegion(effaceable_device_t * device, uint32_t index, const void * buf, uint32_t length);
|
||||
static inline bool readRegion(effaceable_device_t * device, uint32_t index, void * buf, uint32_t length);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline void * allocMem(effaceable_device_t * device, uint32_t size);
|
||||
static inline void freeMem(effaceable_device_t * device, void * buf, uint32_t size);
|
||||
static inline void * setMem(effaceable_device_t * device, void * buf, uint8_t val, uint32_t size);
|
||||
static inline void * moveMem(effaceable_device_t * device, void * dst, const void * src, uint32_t size);
|
||||
static inline int cmpMem(effaceable_device_t * device, const void * lhs, const void * rhs, uint32_t size);
|
||||
static inline void readRandom(effaceable_device_t * device, void * buf, uint32_t size);
|
||||
static inline uint32_t crc32Sys(effaceable_device_t * device, uint32_t crc, const void * buf, uint32_t size, bool finish);
|
||||
static inline bool setPropertySys(effaceable_device_t * device, const char * key, uint32_t value);
|
||||
static inline void panicSys(effaceable_device_t * device, const char * msg);
|
||||
|
||||
static int logf(effaceable_device_t * device, const char * fmt, ...);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void initContext(effaceable_device_t * device, effaceable_system_t * system, effaceable_nor_hal_t * nor, effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
__END_DECLS
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_NOR_CORE_H_ */
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_PRIVATE_TYPES_H_
|
||||
#define _EFFACEABLE_PRIVATE_TYPES_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <AppleEffaceableStorageFormat.h>
|
||||
#include "effaceable_locker.h"
|
||||
#include "effaceable_context.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// grrrr
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define min(a,b) \
|
||||
({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_PRIVATE_TYPES_H_ */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _EFFACEABLE_STORAGE_CORE_H_
|
||||
#define _EFFACEABLE_STORAGE_CORE_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void updateInitialProperties(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t extGetCopiesPerUnit(effaceable_storage_t * storage);
|
||||
static uint32_t extGetCopySize(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t getCopiesPerUnit(effaceable_storage_t * storage);
|
||||
static uint32_t getCopySize(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static EffaceableReturn getLocker(effaceable_storage_t * storage, uint32_t type_id, void *data, uint32_t *data_size, bool untrusted);
|
||||
static EffaceableReturn getLockerExternal(effaceable_storage_t * storage, uint32_t type_id, void *data, uint32_t *data_size, bool untrusted);
|
||||
static EffaceableReturn getLockerInternal(effaceable_storage_t * storage, uint32_t type_id, void *data, uint32_t *data_size, bool untrusted, bool notprivate);
|
||||
static EffaceableReturn setLocker(effaceable_storage_t * storage, uint32_t type_id, const void *data, uint32_t data_size, bool untrusted);
|
||||
static EffaceableReturn setLockerExternal(effaceable_storage_t * storage, uint32_t type_id, const void *data, uint32_t data_size, bool untrusted);
|
||||
static EffaceableReturn setLockerInternal(effaceable_storage_t * storage, uint32_t type_id, const void *data, uint32_t data_size, bool untrusted, bool notprivate);
|
||||
static EffaceableReturn spaceForLocker(effaceable_storage_t * storage, uint32_t type_id, uint32_t *data_space);
|
||||
static EffaceableReturn effaceLocker(effaceable_storage_t * storage, uint32_t type_id, bool untrusted);
|
||||
static EffaceableReturn effaceLockerExternal(effaceable_storage_t * storage, uint32_t type_id, bool untrusted);
|
||||
static EffaceableReturn effaceLockerInternal(effaceable_storage_t * storage, uint32_t type_id, bool untrusted, bool notprivate);
|
||||
static EffaceableReturn getBytes(effaceable_storage_t * storage, void * client_buf, uint32_t offset, uint32_t count);
|
||||
static EffaceableReturn wipeStorage(effaceable_storage_t * storage, void * tmp_buf);
|
||||
static uint32_t getCapacity(effaceable_storage_t * storage);
|
||||
static bool isFormatted(effaceable_storage_t * storage);
|
||||
static EffaceableReturn formatStorage(effaceable_storage_t * storage);
|
||||
static EffaceableReturn generateNonce(effaceable_storage_t * storage, void * hash);
|
||||
static EffaceableReturn consumeNonce(effaceable_storage_t * storage, uint64_t * nonce);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static EffaceableReturn eraseGroupLogged(effaceable_storage_t * storage, uint32_t group);
|
||||
static EffaceableReturn writeUnitLogged(effaceable_storage_t * storage, const void * io_buf, uint32_t group, uint32_t unit);
|
||||
static EffaceableReturn readUnitLogged(effaceable_storage_t * storage, void * io_buf, uint32_t group, uint32_t unit);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void findCurrentClone(effaceable_storage_t * storage, void * tmp_buf, void * scan_buf, void * clone_buf);
|
||||
static bool isGroupEffaced(effaceable_storage_t * storage, void * tmp_buf, uint32_t group);
|
||||
static EffaceableReturn obscureCopy(effaceable_storage_t * storage, void * clear_buf, void * crypt_buf, uint32_t copy);
|
||||
static EffaceableReturn revealCopy(effaceable_storage_t * storage, void * crypt_buf, void * clear_buf, uint32_t copy);
|
||||
static EffaceableReturn commitClone(effaceable_storage_t * storage, void * clone_buf, uint32_t group);
|
||||
static EffaceableReturn writeClone(effaceable_storage_t * storage, void * tmp_buf, void * clone_buf, uint32_t group, uint32_t generation);
|
||||
static uint32_t checksumCopy(effaceable_storage_t * storage, void * clone_buf);
|
||||
static bool isCopyValid(effaceable_storage_t * storage, const void * clone_buf);
|
||||
static EffaceableReturn confirmClone(effaceable_storage_t * storage, void * tmp_buf, void * clone_buf, uint32_t group);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t nextGroup(effaceable_storage_t * storage, uint32_t group);
|
||||
static EffaceableReturn effaceGroup(effaceable_storage_t * storage, void * tmp_buf, uint32_t group);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static locker_t * findLockerForRequest(effaceable_storage_t * storage, uint32_t type_id);
|
||||
static EffaceableReturn discardLockers(effaceable_storage_t * storage);
|
||||
static void appendSentinel(effaceable_storage_t * storage);
|
||||
static locker_t * findLockerForRequest(effaceable_storage_t * storage, uint32_t type_id);
|
||||
static EffaceableReturn getLockersFromStorage(effaceable_storage_t * storage);
|
||||
static EffaceableReturn putLockersInStorage(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void cleanBuffers(effaceable_storage_t * storage);
|
||||
static void foundCurrentCloneInGroup(effaceable_storage_t * storage, uint32_t group);
|
||||
static void updateDynamicProperties(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void initContract(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline void * getCache(effaceable_storage_t * storage);
|
||||
static inline void * setCache(effaceable_storage_t * storage, void * buf);
|
||||
|
||||
static inline void * getScratch(effaceable_storage_t * storage);
|
||||
static inline void * setScratch(effaceable_storage_t * storage, void * buf);
|
||||
|
||||
static inline void * getSniff(effaceable_storage_t * storage);
|
||||
static inline void * setSniff(effaceable_storage_t * storage, void * buf);
|
||||
|
||||
static inline void * getCrypt(effaceable_storage_t * storage);
|
||||
static inline void * setCrypt(effaceable_storage_t * storage, void * buf);
|
||||
|
||||
static inline void * getNonceHash(effaceable_storage_t * storage);
|
||||
static inline void * setNonceHash(effaceable_storage_t * storage, void * buf);
|
||||
|
||||
static inline uint32_t getGeneration(effaceable_storage_t * storage);
|
||||
static inline uint32_t setGeneration(effaceable_storage_t * storage, uint32_t generation);
|
||||
|
||||
static inline uint32_t getGroup(effaceable_storage_t * storage);
|
||||
static inline uint32_t setGroup(effaceable_storage_t * storage, uint32_t group);
|
||||
|
||||
static inline bool getIsFormatted(effaceable_storage_t * storage);
|
||||
static inline bool setIsFormatted(effaceable_storage_t * storage, bool is_formatted);
|
||||
|
||||
static inline bool getFullScanEnabled(effaceable_storage_t * storage);
|
||||
static inline bool setFullScanEnabled(effaceable_storage_t * storage, bool enable);
|
||||
|
||||
static inline bool getWipeEnabled(effaceable_storage_t * storage);
|
||||
static inline bool setWipeEnabled(effaceable_storage_t * storage, bool enable);
|
||||
|
||||
static inline locker_list_t * getLockerList(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline uint32_t getGroupCount(effaceable_storage_t * storage);
|
||||
static inline uint32_t getUnitsPerGroup(effaceable_storage_t * storage);
|
||||
static inline uint32_t getUnitSize(effaceable_storage_t * storage);
|
||||
static inline bool eraseGroup(effaceable_storage_t * storage, uint32_t group);
|
||||
static inline bool writeUnit(effaceable_storage_t * storage, const void * buf, uint32_t group, uint32_t unit);
|
||||
static inline bool readUnit(effaceable_storage_t * storage, void * buf, uint32_t group, uint32_t unit);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline void deviceCleanBuffers(effaceable_storage_t * storage);
|
||||
static inline void deviceFoundCurrentCloneInGroup(effaceable_storage_t * storage, uint32_t group);
|
||||
static inline void deviceUpdateDynamicProperties(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static inline void * allocMem(effaceable_storage_t * storage, uint32_t size);
|
||||
static inline void freeMem(effaceable_storage_t * storage, void * buf, uint32_t size);
|
||||
static inline void * setMem(effaceable_storage_t * storage, void * buf, uint8_t val, uint32_t size);
|
||||
static inline void * moveMem(effaceable_storage_t * storage, void * dst, const void * src, uint32_t size);
|
||||
static inline int cmpMem(effaceable_storage_t * storage, const void * lhs, const void * rhs, uint32_t size);
|
||||
static inline void readRandom(effaceable_storage_t * storage, void * buf, uint32_t size);
|
||||
static inline void calcSHA1(effaceable_storage_t * storage, const void * buf, uint32_t size, void * hash);
|
||||
static inline uint32_t crc32Sys(effaceable_storage_t * storage, uint32_t crc, const void * buf, uint32_t size, bool finish);
|
||||
static inline bool setPropertySys(effaceable_storage_t * storage, const char * key, uint32_t value);
|
||||
static inline void panicSys(effaceable_storage_t * storage, const char * msg);
|
||||
|
||||
static int logf(effaceable_storage_t * storage, const char * fmt, ...);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void initContext(effaceable_storage_t * storage, effaceable_device_t * device, effaceable_system_t * system);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
__END_DECLS
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_STORAGE_CORE_H_ */
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// XXX find a better name for this file. perhaps 'effaceable_storage_entry.h'?
|
||||
|
||||
#ifndef _EFFACEABLE_STORAGE_DEV_H_
|
||||
#define _EFFACEABLE_STORAGE_DEV_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
extern void startEffaceableStorage(effaceable_storage_t * storage, effaceable_device_t * device, effaceable_system_t * system);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
__END_DECLS
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_STORAGE_DEV_H_ */
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _APPLE_EFFACEABLE_STORAGE_FORMAT_H
|
||||
#define _APPLE_EFFACEABLE_STORAGE_FORMAT_H
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define kAppleEffaceableStorageMagic 'Face'
|
||||
|
||||
#define kAppleEffaceableStorageGenerationUnknown ((uint32_t)0x00000000)
|
||||
#define kAppleEffaceableStorageGenerationInitial ((uint32_t)0x00000001)
|
||||
#define kAppleEffaceableStorageGenerationInvalid ((uint32_t)0xFFFFFFFF)
|
||||
|
||||
#define kAppleEffaceableStorageVersionMajorUnknown ((uint32_t)0x00000000)
|
||||
#define kAppleEffaceableStorageVersionMajorInitial ((uint32_t)0x00000001)
|
||||
#define kAppleEffaceableStorageVersionMajorNonce ((uint32_t)0x00000001)
|
||||
#define kAppleEffaceableStorageVersionMajorCurrent kAppleEffaceableStorageVersionMajorNonce
|
||||
#define kAppleEffaceableStorageVersionMajorInvalid ((uint32_t)0xFFFFFFFF)
|
||||
|
||||
#define kAppleEffaceableStorageVersionMinorUnknown ((uint32_t)0x00000000)
|
||||
#define kAppleEffaceableStorageVersionMinorInitial ((uint32_t)0x00000001)
|
||||
#define kAppleEffaceableStorageVersionMinorNonce ((uint32_t)0x00000002)
|
||||
#define kAppleEffaceableStorageVersionMinorCurrent kAppleEffaceableStorageVersionMajorNonce
|
||||
#define kAppleEffaceableStorageVersionMinorInvalid ((uint32_t)0xFFFFFFFF)
|
||||
|
||||
#define kAppleEffaceableStorageFlagsNone ((uint32_t)0x00000000)
|
||||
|
||||
#define kAppleEffaceableStorageSize 1024
|
||||
#define kAppleEffaceableStorageSaltSize 16
|
||||
#define kAppleEffaceableStorageReservedSize 24
|
||||
#define kAppleEffaceableStorageContentSize (kAppleEffaceableStorageSize - sizeof(AppleEffaceableStorageHeader))
|
||||
|
||||
#define kAppleEffaceableStorageLockerBase 0
|
||||
#define kAppleEffaceableStorageLockerSize (kAppleEffaceableStorageContentSize - kAppleEffaceableStorageLockerBase)
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// All structures defined here are on-media formats.
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic;
|
||||
uint32_t version_major;
|
||||
uint32_t version_minor;
|
||||
uint32_t flags;
|
||||
|
||||
} __attribute__((__packed__)) AppleEffaceableStorageNaked;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reserved[kAppleEffaceableStorageReservedSize];
|
||||
uint32_t generation;
|
||||
uint32_t checksum;
|
||||
|
||||
} __attribute__((__packed__)) AppleEffaceableStorageVeiled;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AppleEffaceableStorageNaked naked;
|
||||
uint8_t salt[kAppleEffaceableStorageSaltSize];
|
||||
AppleEffaceableStorageVeiled veiled;
|
||||
|
||||
} __attribute__((__packed__)) AppleEffaceableStorageHeader;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AppleEffaceableStorageHeader header;
|
||||
uint8_t content[kAppleEffaceableStorageContentSize];
|
||||
|
||||
} __attribute__((__packed__)) AppleEffaceableStorageClone;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t magic;
|
||||
uint16_t data_size;
|
||||
uint32_t type_id;
|
||||
} __attribute__((__packed__)) AppleEffaceableStorageLockerHeader;
|
||||
|
||||
#define kAppleEffaceableStorageLockerMagic 0x4c6b // 'Lk'
|
||||
#define kAppleEffaceableStorageLockerSentinel 'DONE'
|
||||
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _APPLE_EFFACEABLE_STORAGE_FORMAT_H */
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _APPLE_EFFACEABLE_STORAGE_KEYS_H
|
||||
#define _APPLE_EFFACEABLE_STORAGE_KEYS_H
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// callPlatformFunction methods
|
||||
//
|
||||
// GetLocker
|
||||
// Returns the contents of the locker type_id, if it exists.
|
||||
//
|
||||
// SetLocker
|
||||
// Updates or creates the locker type_id.
|
||||
//
|
||||
// Space
|
||||
// Returns the largest amount of data that could be stored in a locker
|
||||
// with type_id, accounting for overwriting any current value.
|
||||
//
|
||||
// Efface
|
||||
// Destroys the locker type_id and all its contents.
|
||||
//
|
||||
// GenerateNonce
|
||||
// Populate private nonce locker with random 64-bit value and return its hash.
|
||||
//
|
||||
// type_id
|
||||
// Unique identifier for the locker. Typically a four-byte ASCII value.
|
||||
//
|
||||
// data
|
||||
// Pointer to read/write data buffer.
|
||||
//
|
||||
// data_size
|
||||
// Buffer/operation size in bytes. Note that for GetLocker this is a
|
||||
// pointer to an IOByteCount that is updated to the actual size of the
|
||||
// locker, and for Space this is the amount of space available for
|
||||
// a locker with type_id.
|
||||
//
|
||||
// untrusted
|
||||
// True if the client is untrusted and should not be allowed to operate
|
||||
// on protected lockers.
|
||||
//
|
||||
// hash
|
||||
// Pointer to 20 byte buffer into which the nonce hash will be copied.
|
||||
//
|
||||
#define kAppleEffaceableStorageFunctionGet "GetLocker" // (type_id, *data, *data_size, untrusted)
|
||||
#define kAppleEffaceableStorageFunctionSet "SetLocker" // (type_id, *data, data_size, untrusted)
|
||||
#define kAppleEffaceableStorageFunctionEfface "EffaceLocker" // (type_id, untrusted)
|
||||
#define kAppleEffaceableStorageFunctionGenerateNonce "GenerateNonce" // (*hash)
|
||||
#define kAppleEffaceableStorageFunctionSpace "Space" // (type_id, *free_size)
|
||||
|
||||
// When set on a request's type_id, the locker can only be read, overwritten and
|
||||
// effaced by a kernel client. Cannot be set by a user client.
|
||||
#define kAppleEffaceableStorageLockerProtected (1<<31)
|
||||
// Special type for manipulating nonce locker, which is permitted
|
||||
// only by the effaceable storage driver
|
||||
#define kAppleEffaceableStorageLockerPrivate (1<<23)
|
||||
|
||||
// These bits must not be set in a locker's type_id.
|
||||
#define kAppleEffaceableStorageLockerReserved ((1<<15) | (1<<7))
|
||||
|
||||
// This type_id is considered a wildcard; passed to Efface it will cause
|
||||
// every locker to be effaced.
|
||||
#define kAppleEffaceableStorageLockerWildcard '****'
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define kAppleEffaceableStorageClassName "AppleEffaceableStorage"
|
||||
|
||||
// DeviceTree properties
|
||||
|
||||
#define kAppleEffaceableStoragePropertyVersionMajor "VersionMajor"
|
||||
#define kAppleEffaceableStoragePropertyVersionMinor "VersionMinor"
|
||||
#define kAppleEffaceableStoragePropertyIsFormatted "IsFormatted"
|
||||
|
||||
// UserClient methods
|
||||
|
||||
enum _AppleEffaceableStorageMethod
|
||||
{
|
||||
// Maintenance interface.
|
||||
kAppleEffaceableStorageMethodGetCapacity,
|
||||
kAppleEffaceableStorageMethodGetBytes,
|
||||
kAppleEffaceableStorageMethodSetBytes,
|
||||
kAppleEffaceableStorageMethodIsFormatted,
|
||||
kAppleEffaceableStorageMethodFormatStorage,
|
||||
|
||||
// Userclient locker methods.
|
||||
//
|
||||
// The type_id argument is always passed in scalarInput[0].
|
||||
//
|
||||
// GetLocker returns the actual size of the locker in scalarOutput[0],
|
||||
// and if structureOutput/structureOutputSize describe a buffer, as much
|
||||
// of the locker data as will fit is returned into the buffer.
|
||||
//
|
||||
// SetLocker creates or replaces an existing locker with the contents of
|
||||
// structureInput/structureInputSize.
|
||||
//
|
||||
// EffaceLocker destroys the locker.
|
||||
//
|
||||
kAppleEffaceableStorageMethodGetLocker,
|
||||
kAppleEffaceableStorageMethodSetLocker,
|
||||
kAppleEffaceableStorageMethodEffaceLocker,
|
||||
kAppleEffaceableStorageMethodSpace,
|
||||
|
||||
// Debug-only interface.
|
||||
kAppleEffaceableStorageMethodWipeStorage,
|
||||
|
||||
// Nonce support
|
||||
kAppleEffaceableStorageMethodGenerateNonce,
|
||||
|
||||
kAppleEffaceableStorageMethodCount
|
||||
};
|
||||
|
||||
typedef enum _AppleEffaceableStorageMethod AppleEffaceableStorageMethod;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _APPLE_EFFACEABLE_STORAGE_KEYS_H */
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_CONTRACT_H_
|
||||
#define _EFFACEABLE_CONTRACT_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* horrible that this is necessary */
|
||||
#ifndef __cplusplus
|
||||
# ifndef false
|
||||
# include <stdbool.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
|
||||
enum _EffaceableReturn
|
||||
{
|
||||
kEffaceableReturnSuccess = 0,
|
||||
kEffaceableReturnError,
|
||||
|
||||
kEffaceableReturnNotFound,
|
||||
kEffaceableReturnUnsupported,
|
||||
kEffaceableReturnNotPrivileged,
|
||||
kEffaceableReturnNotPermitted,
|
||||
kEffaceableReturnBadArgument,
|
||||
kEffaceableReturnBadMedia,
|
||||
kEffaceableReturnUnformattedMedia,
|
||||
kEffaceableReturnIOError,
|
||||
kEffaceableReturnNoMemory,
|
||||
kEffaceableReturnNoSpace,
|
||||
kEffaceableReturnInternalError,
|
||||
|
||||
kEffaceableReturnInvalid
|
||||
};
|
||||
|
||||
typedef enum _EffaceableReturn EffaceableReturn;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
struct _effaceable_storage;
|
||||
struct _effaceable_device;
|
||||
struct _effaceable_system;
|
||||
struct _effaceable_nor_hal;
|
||||
struct _effaceable_nand_hal;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct _effaceable_storage effaceable_storage_t;
|
||||
typedef struct _effaceable_device effaceable_device_t;
|
||||
|
||||
typedef struct _effaceable_system effaceable_system_t;
|
||||
typedef struct _effaceable_nor_hal effaceable_nor_hal_t;
|
||||
typedef struct _effaceable_nand_hal effaceable_nand_hal_t;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
struct _effaceable_storage
|
||||
{
|
||||
void * opaque;
|
||||
|
||||
/* client api for use externally */
|
||||
EffaceableReturn (* getLocker )(effaceable_storage_t * storage, uint32_t type_id, void *data, uint32_t *data_size, bool untrusted);
|
||||
EffaceableReturn (* setLocker )(effaceable_storage_t * storage, uint32_t type_id, const void *data, uint32_t data_size, bool untrusted);
|
||||
EffaceableReturn (* spaceForLocker )(effaceable_storage_t * storage, uint32_t type_id, uint32_t *data_space);
|
||||
EffaceableReturn (* effaceLocker )(effaceable_storage_t * storage, uint32_t type_id, bool untrusted);
|
||||
EffaceableReturn (* generateNonce )(effaceable_storage_t * storage, void * hash);
|
||||
EffaceableReturn (* consumeNonce )(effaceable_storage_t * storage, uint64_t * nonce);
|
||||
|
||||
/* management api for use externally */
|
||||
EffaceableReturn (* getBytes )(effaceable_storage_t * storage, void * client_buf, uint32_t offset, uint32_t count);
|
||||
EffaceableReturn (* wipeStorage )(effaceable_storage_t * storage, void * tmp_buf);
|
||||
uint32_t (* getCapacity )(effaceable_storage_t * storage);
|
||||
bool (* isFormatted )(effaceable_storage_t * storage);
|
||||
EffaceableReturn (* formatStorage )(effaceable_storage_t * storage);
|
||||
|
||||
/* spi for use by device-specific controller */
|
||||
uint32_t (* getCopiesPerUnit )(effaceable_storage_t * storage);
|
||||
uint32_t (* getCopySize )(effaceable_storage_t * storage);
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct _effaceable_device
|
||||
{
|
||||
void * opaque;
|
||||
|
||||
/* device controller must provide concrete implementation of these operations */
|
||||
uint32_t (* getGroupCount )(effaceable_device_t * device);
|
||||
uint32_t (* getUnitsPerGroup )(effaceable_device_t * device);
|
||||
uint32_t (* getUnitSize )(effaceable_device_t * device);
|
||||
EffaceableReturn (* eraseGroup )(effaceable_device_t * device, uint32_t group);
|
||||
EffaceableReturn (* writeUnit )(effaceable_device_t * device, const void * buf, uint32_t group, uint32_t unit);
|
||||
EffaceableReturn (* readUnit )(effaceable_device_t * device, void * buf, uint32_t group, uint32_t unit);
|
||||
|
||||
/* device controller may optionally extend these device; if NULL, code should not call */
|
||||
void (* cleanBuffers )(effaceable_device_t * device);
|
||||
void (* foundCurrentCloneInGroup )(effaceable_device_t * device, uint32_t group);
|
||||
void (* updateDynamicProperties )(effaceable_device_t * device);
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct _effaceable_system
|
||||
{
|
||||
void * opaque;
|
||||
|
||||
/* operating system specific operations must be supplied when starting device */
|
||||
void * (* allocMem )(effaceable_system_t * system, uint32_t size);
|
||||
void (* freeMem )(effaceable_system_t * system, void * buf, uint32_t size);
|
||||
void * (* setMem )(effaceable_system_t * system, void * buf, uint8_t val, uint32_t size);
|
||||
void * (* moveMem )(effaceable_system_t * system, void * dst, const void * src, uint32_t size);
|
||||
int (* cmpMem )(effaceable_system_t * system, const void * lhs, const void * rhs, uint32_t size);
|
||||
void (* readRandom )(effaceable_system_t * system, void * buf, uint32_t size);
|
||||
void (* calcSHA1 )(effaceable_system_t * system, const void * buf, uint32_t size, void * hash);
|
||||
uint32_t (* crc32 )(effaceable_system_t * system, uint32_t crc, const void * buf, uint32_t size, bool finish);
|
||||
bool (* setProperty )(effaceable_system_t * system, const char * key, uint32_t value);
|
||||
void (* panicSys )(effaceable_system_t * system, const char * msg);
|
||||
int (* vlogf )(effaceable_system_t * system, const char * fmt, va_list ap);
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/* contract for NOR HAL operations */
|
||||
struct _effaceable_nor_hal
|
||||
{
|
||||
void * opaque;
|
||||
|
||||
/* NOR HAL operations must be supplied when starting device */
|
||||
uint32_t (* getRegionCount )(effaceable_nor_hal_t * nor);
|
||||
uint32_t (* getRegionSize )(effaceable_nor_hal_t * nor, uint32_t index);
|
||||
EffaceableReturn (* eraseRegion )(effaceable_nor_hal_t * nor, uint32_t index, uint32_t length);
|
||||
uint32_t (* writeRegion )(effaceable_nor_hal_t * nor, uint32_t index, const void * buf, uint32_t length);
|
||||
bool (* readRegion )(effaceable_nor_hal_t * nor, uint32_t index, void * buf, uint32_t length);
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/* contract for NAND HAL operations */
|
||||
struct _effaceable_nand_hal
|
||||
{
|
||||
void * opaque;
|
||||
|
||||
/* NAND HAL operations must be supplied when starting device */
|
||||
uint32_t (* getPageSize )(effaceable_nand_hal_t * nand);
|
||||
uint32_t (* getPageCount )(effaceable_nand_hal_t * nand);
|
||||
uint32_t (* getBlockCount )(effaceable_nand_hal_t * nand);
|
||||
uint32_t (* getBankCount )(effaceable_nand_hal_t * nand);
|
||||
bool (* isBlockBad )(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block);
|
||||
EffaceableReturn (* eraseBlock )(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block);
|
||||
EffaceableReturn (* writePage )(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length);
|
||||
EffaceableReturn (* readPage )(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length);
|
||||
bool (* requestPartitionTableDiff )(effaceable_nand_hal_t * nand, void * buf, uint32_t length);
|
||||
bool (* providePartitionTableDiff )(effaceable_nand_hal_t * nand, void * buf, uint32_t length);
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
extern void startEffaceableNOR(effaceable_device_t * device, effaceable_system_t * system, effaceable_nor_hal_t * nor, effaceable_storage_t * storage);
|
||||
extern void startEffaceableNAND(effaceable_device_t * device, effaceable_system_t * system, effaceable_nand_hal_t * nor, effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
__END_DECLS
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_CONTRACT_H_ */
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_DEBUG_H_
|
||||
#define _EFFACEABLE_DEBUG_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define EFFACEABLE_DBGMSK_INIT (1<<0)
|
||||
#define EFFACEABLE_DBGMSK_ERR (1<<1)
|
||||
#define EFFACEABLE_DBGMSK_WARN (1<<2)
|
||||
#define EFFACEABLE_DBGMSK_INFO (1<<3)
|
||||
#define EFFACEABLE_DBGMSK_SPEW (1<<4)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define EFFACEABLE_DBGTAG_INIT "INIT"
|
||||
#define EFFACEABLE_DBGTAG_ERR "ERR "
|
||||
#define EFFACEABLE_DBGTAG_WARN "WARN"
|
||||
#define EFFACEABLE_DBGTAG_INFO "INFO"
|
||||
#define EFFACEABLE_DBGTAG_SPEW "SPEW"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define EFFACEABLE_DEBUG_ALL (EFFACEABLE_DBGMSK_INIT | \
|
||||
EFFACEABLE_DBGMSK_ERR | \
|
||||
EFFACEABLE_DBGMSK_WARN | \
|
||||
EFFACEABLE_DBGMSK_INFO | \
|
||||
EFFACEABLE_DBGMSK_SPEW)
|
||||
|
||||
#define EFFACEABLE_DEBUG_MOST (EFFACEABLE_DBGMSK_INIT | \
|
||||
EFFACEABLE_DBGMSK_ERR | \
|
||||
EFFACEABLE_DBGMSK_WARN | \
|
||||
EFFACEABLE_DBGMSK_INFO)
|
||||
|
||||
#define EFFACEABLE_DEBUG_DEFAULT (EFFACEABLE_DBGMSK_INIT | \
|
||||
EFFACEABLE_DBGMSK_ERR)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define EFFACEABLE_DEBUG EFFACEABLE_DEBUG_DEFAULT
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if defined(EFFACEABLE_DEBUG) && EFFACEABLE_DEBUG
|
||||
|
||||
# define dlogf(_api, _fac, _fmt, ...) \
|
||||
do { \
|
||||
if (EFFACEABLE_DBGMSK_##_fac & \
|
||||
EFFACEABLE_DEBUG) { \
|
||||
logf(_api, "[effaceable:%s] " _fmt "\n", \
|
||||
EFFACEABLE_DBGTAG_##_fac , \
|
||||
## __VA_ARGS__ ); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
# define dhexdump(_api, _fac, _buf, _count) \
|
||||
do { \
|
||||
if (EFFACEABLE_DBGMSK_##_fac & \
|
||||
EFFACEABLE_DEBUG) { \
|
||||
unsigned _i, _j; \
|
||||
for (_i = 0; _i < _count; _i += 16) { \
|
||||
logf(_api, "[effaceable:%s] 0x%08x:", \
|
||||
EFFACEABLE_DBGTAG_##_fac , \
|
||||
((unsigned) _buf) + _i); \
|
||||
for (_j = 0; (_j < 16) && ((_i + _j) < _count); _j++) \
|
||||
logf(_api, " %2.2x", ((uint8_t*)_buf)[_i + _j]); \
|
||||
logf(_api, "\n"); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#else
|
||||
# define dlogf(...) do { /* nothing */ } while(0)
|
||||
# define dhexdump(...) do { /* nothing */ } while(0)
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_DEBUG_H_ */
|
||||
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. The rights granted to you under the License
|
||||
* may not be used to create, or enable the creation or redistribution of,
|
||||
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||
* circumvent, violate, or enable the circumvention or violation of, any
|
||||
* terms of an Apple operating system software license agreement.
|
||||
*
|
||||
* Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* singly-linked tail queues, lists, tail queues, and circular queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A singly-linked tail queue is headed by a pair of pointers, one to the
|
||||
* head of the list and the other to the tail of the list. The elements are
|
||||
* singly linked for minimum space and pointer manipulation overhead at the
|
||||
* expense of O(n) removal for arbitrary elements. New elements can be added
|
||||
* to the list after an existing element, at the head of the list, or at the
|
||||
* end of the list. Elements being removed from the head of the tail queue
|
||||
* should use the explicit macro for this purpose for optimum efficiency.
|
||||
* A singly-linked tail queue may only be traversed in the forward direction.
|
||||
* Singly-linked tail queues are ideal for applications with large datasets
|
||||
* and few or no removals or for implementing a FIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
* Note that circle queues are deprecated, because, as the removal log
|
||||
* in FreeBSD states, "CIRCLEQs are a disgrace to everything Knuth taught
|
||||
* us in Volume 1 Chapter 2. [...] Use TAILQ instead, it provides the same
|
||||
* functionality." Code using them will continue to compile, but they
|
||||
* are no longer documented on the man page.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*
|
||||
*
|
||||
* SLIST LIST STAILQ TAILQ CIRCLEQ
|
||||
* _HEAD + + + + +
|
||||
* _HEAD_INITIALIZER + + + + -
|
||||
* _ENTRY + + + + +
|
||||
* _INIT + + + + +
|
||||
* _EMPTY + + + + +
|
||||
* _FIRST + + + + +
|
||||
* _NEXT + + + + +
|
||||
* _PREV - - - + +
|
||||
* _LAST - - + + +
|
||||
* _FOREACH + + + + +
|
||||
* _FOREACH_SAFE + + + + -
|
||||
* _FOREACH_REVERSE - - - + -
|
||||
* _FOREACH_REVERSE_SAFE - - - + -
|
||||
* _INSERT_HEAD + + + + +
|
||||
* _INSERT_BEFORE - + - + +
|
||||
* _INSERT_AFTER + + + + +
|
||||
* _INSERT_TAIL - - + + +
|
||||
* _CONCAT - - + + -
|
||||
* _REMOVE_AFTER + - + - -
|
||||
* _REMOVE_HEAD + - + - -
|
||||
* _REMOVE_HEAD_UNTIL - - + - -
|
||||
* _REMOVE + + + + +
|
||||
* _SWAP - + + + -
|
||||
*
|
||||
*/
|
||||
#ifdef QUEUE_MACRO_DEBUG
|
||||
/* Store the last 2 places the queue element or head was altered */
|
||||
struct qm_trace {
|
||||
char * lastfile;
|
||||
int lastline;
|
||||
char * prevfile;
|
||||
int prevline;
|
||||
};
|
||||
|
||||
#define TRACEBUF struct qm_trace trace;
|
||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
||||
|
||||
#define QMD_TRACE_HEAD(head) do { \
|
||||
(head)->trace.prevline = (head)->trace.lastline; \
|
||||
(head)->trace.prevfile = (head)->trace.lastfile; \
|
||||
(head)->trace.lastline = __LINE__; \
|
||||
(head)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TRACE_ELEM(elem) do { \
|
||||
(elem)->trace.prevline = (elem)->trace.lastline; \
|
||||
(elem)->trace.prevfile = (elem)->trace.lastfile; \
|
||||
(elem)->trace.lastline = __LINE__; \
|
||||
(elem)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define QMD_TRACE_ELEM(elem)
|
||||
#define QMD_TRACE_HEAD(head)
|
||||
#define TRACEBUF
|
||||
#define TRASHIT(x)
|
||||
#endif /* QUEUE_MACRO_DEBUG */
|
||||
|
||||
/*
|
||||
* Singly-linked List declarations.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
||||
for ((varp) = &SLIST_FIRST((head)); \
|
||||
((var) = *(varp)) != NULL; \
|
||||
(varp) = &SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_INIT(head) do { \
|
||||
SLIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if (SLIST_FIRST((head)) == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = SLIST_FIRST((head)); \
|
||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
||||
curelm = SLIST_NEXT(curelm, field); \
|
||||
SLIST_REMOVE_AFTER(curelm, field); \
|
||||
} \
|
||||
TRASHIT((elm)->field.sle_next); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
SLIST_NEXT(elm, field) = \
|
||||
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first;/* first element */ \
|
||||
struct type **stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_LAST(head, type, field) \
|
||||
(STAILQ_EMPTY((head)) ? \
|
||||
NULL : \
|
||||
((struct type *)(void *) \
|
||||
((char *)((head)->stqh_last) - __offsetof(struct type, field))))
|
||||
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = STAILQ_FIRST((head)); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
TRASHIT((elm)->field.stqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_SWAP(head1, head2, type) do { \
|
||||
struct type *swap_first = STAILQ_FIRST(head1); \
|
||||
struct type **swap_last = (head1)->stqh_last; \
|
||||
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_FIRST(head2) = swap_first; \
|
||||
(head2)->stqh_last = swap_last; \
|
||||
if (STAILQ_EMPTY(head1)) \
|
||||
(head1)->stqh_last = &STAILQ_FIRST(head1); \
|
||||
if (STAILQ_EMPTY(head2)) \
|
||||
(head2)->stqh_last = &STAILQ_FIRST(head2); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* List declarations.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
|
||||
#if (defined(_KERNEL) && defined(INVARIANTS)) || defined(QUEUE_MACRO_DEBUG)
|
||||
#define QMD_LIST_CHECK_HEAD(head, field) do { \
|
||||
if (LIST_FIRST((head)) != NULL && \
|
||||
LIST_FIRST((head))->field.le_prev != \
|
||||
&LIST_FIRST((head))) \
|
||||
panic("Bad list head %p first->prev != head", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
|
||||
if (LIST_NEXT((elm), field) != NULL && \
|
||||
LIST_NEXT((elm), field)->field.le_prev != \
|
||||
&((elm)->field.le_next)) \
|
||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_PREV(elm, field) do { \
|
||||
if (*(elm)->field.le_prev != (elm)) \
|
||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define QMD_LIST_CHECK_HEAD(head, field)
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field)
|
||||
#define QMD_LIST_CHECK_PREV(elm, field)
|
||||
#endif /* (_KERNEL && INVARIANTS) || QUEUE_MACRO_DEBUG */
|
||||
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_NEXT(listelm, field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
|
||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
||||
&LIST_NEXT((elm), field); \
|
||||
LIST_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_PREV(listelm, field); \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
LIST_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
QMD_LIST_CHECK_HEAD((head), field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
|
||||
LIST_FIRST((head)) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
QMD_LIST_CHECK_NEXT(elm, field); \
|
||||
QMD_LIST_CHECK_PREV(elm, field); \
|
||||
if (LIST_NEXT((elm), field) != NULL) \
|
||||
LIST_NEXT((elm), field)->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
||||
TRASHIT((elm)->field.le_next); \
|
||||
TRASHIT((elm)->field.le_prev); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_tmp = LIST_FIRST((head1)); \
|
||||
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
|
||||
LIST_FIRST((head2)) = swap_tmp; \
|
||||
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
|
||||
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue declarations.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
QMD_TRACE_HEAD(head1); \
|
||||
QMD_TRACE_HEAD(head2); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_INIT(head) do { \
|
||||
TAILQ_FIRST((head)) = NULL; \
|
||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else { \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
TAILQ_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&listelm->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
TAILQ_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&listelm->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
TAILQ_FIRST((head)) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
TAILQ_NEXT((elm), field) = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else { \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
||||
TRASHIT((elm)->field.tqe_next); \
|
||||
TRASHIT((elm)->field.tqe_prev); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_first = (head1)->tqh_first; \
|
||||
struct type **swap_last = (head1)->tqh_last; \
|
||||
(head1)->tqh_first = (head2)->tqh_first; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
(head2)->tqh_first = swap_first; \
|
||||
(head2)->tqh_last = swap_last; \
|
||||
if ((swap_first = (head1)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head1)->tqh_first; \
|
||||
else \
|
||||
(head1)->tqh_last = &(head1)->tqh_first; \
|
||||
if ((swap_first = (head2)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head2)->tqh_first; \
|
||||
else \
|
||||
(head2)->tqh_last = &(head2)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
|
||||
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for((var) = (head)->cqh_first; \
|
||||
(var) != (void *)(head); \
|
||||
(var) = (var)->field.cqe_next)
|
||||
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = (void *)(head); \
|
||||
(head)->cqh_last = (void *)(head); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = (void *)(head); \
|
||||
if ((head)->cqh_last == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (void *)(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
|
||||
#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
|
||||
|
||||
#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
} while (0)
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#if NOTFB31
|
||||
|
||||
/*
|
||||
* XXX insque() and remque() are an old way of handling certain queues.
|
||||
* They bogusly assumes that all queue heads look alike.
|
||||
*/
|
||||
|
||||
struct quehead {
|
||||
struct quehead *qh_link;
|
||||
struct quehead *qh_rlink;
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
static __inline void
|
||||
insque(void *a, void *b)
|
||||
{
|
||||
struct quehead *element = (struct quehead *)a,
|
||||
*head = (struct quehead *)b;
|
||||
|
||||
element->qh_link = head->qh_link;
|
||||
element->qh_rlink = head;
|
||||
head->qh_link = element;
|
||||
element->qh_link->qh_rlink = element;
|
||||
}
|
||||
|
||||
static __inline void
|
||||
remque(void *a)
|
||||
{
|
||||
struct quehead *element = (struct quehead *)a;
|
||||
|
||||
element->qh_link->qh_rlink = element->qh_rlink;
|
||||
element->qh_rlink->qh_link = element->qh_link;
|
||||
element->qh_rlink = 0;
|
||||
}
|
||||
|
||||
#else /* !__GNUC__ */
|
||||
|
||||
void insque(void *a, void *b);
|
||||
void remque(void *a);
|
||||
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
#endif
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
GLOBAL_INCLUDES += \
|
||||
$(LOCAL_DIR)/include
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/effaceable_storage_core.o \
|
||||
$(LOCAL_DIR)/effaceable_nand_core.o \
|
||||
$(LOCAL_DIR)/effaceable_nor_core.o
|
||||
@@ -0,0 +1,18 @@
|
||||
Significant changes were made to the structure of the project in the transition between AppleEffaceableStorage-24 and AppleEffaceableStorage-25.
|
||||
|
||||
To assist auditors with their task (and anyone else analyzing the history of the project), it is useful to observe the following points:
|
||||
|
||||
* core logic was refactored from the prior classes into a portable core implementation, located in the 'common' directory
|
||||
- compare previous ./AppleEffaceableStorage.cpp to current ./common/effaceable_storage_core.c
|
||||
- compare previous ./AppleEffaceableNAND.cpp to current ./common/effaceable_nand_core.c
|
||||
- compare previous ./AppleEffaceableNOR.cpp to current ./common/effaceable_nor_core.c
|
||||
* additional directory restructuring was done to make major implementation components more obvious to the reader
|
||||
- private headers made available for exither external or internal cross-component use were moved to ./include subdirectory
|
||||
- remaining IOKit code was moved to the ./iokit subdirectory
|
||||
- implementation of the 'effaceable' command line utility was moved to the ./ios subdirectory
|
||||
* more other
|
||||
- there was also a minor bit of new functionality added to support generation and consumption of the private nonce locker
|
||||
- the 'moveMem' system function contract uses 'memmove' arg ordering, so 'bcopy' instances replaced have swapped pointer args
|
||||
- isCopyValid() modified to report real errors; in doing so, structure of logic changed such that all prior checks are negated
|
||||
- deleted all trailing whitespace (with exception of 'queue.h', since it is imported from 'xnu/bsd/sys/queue.h')
|
||||
- converted any tabs that crept in to four space hard indent to match style in majority of project (exception: 'queue.h')
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 <AppleEffaceableStorageFormat.h>
|
||||
#include <AppleEffaceableStorageKeys.h>
|
||||
|
||||
#include <lib/effaceable.h>
|
||||
|
||||
#include <debug.h>
|
||||
#include <sys/menu.h>
|
||||
#include <drivers/sha1.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int do_effaceable(int argc, struct cmd_arg *args);
|
||||
static void printUsage(int argc, struct cmd_arg *args);
|
||||
static void effaceable_hexdump(const void * buf, uint32_t count);
|
||||
static int lockerList(void);
|
||||
|
||||
#if DEBUG_BUILD
|
||||
static int consumeNonce(void);
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int
|
||||
do_effaceable(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (1 >= argc) {
|
||||
printUsage(argc, args);
|
||||
err = 0;
|
||||
} else if (0 == strcmp("lockers", args[1].str)) {
|
||||
err = lockerList();
|
||||
#if DEBUG_BUILD
|
||||
} else if (0 == strcmp("nonce", args[1].str)) {
|
||||
err = consumeNonce();
|
||||
#endif
|
||||
} else {
|
||||
printUsage(argc, args);
|
||||
printf("unrecognized subcommand\n");
|
||||
err = -1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static void
|
||||
printUsage(int argc, struct cmd_arg *args)
|
||||
{
|
||||
printf("USAGE: %s <subcmd>\n\n", ((0 < argc) ? args[0].str : "?"));
|
||||
printf(" Where <subcmd> is one of following\n\n");
|
||||
printf(" lockers - list lockers and contents\n");
|
||||
|
||||
#if DEBUG_BUILD
|
||||
printf(" nonce - consume nonce; then, report its SHA1 hash and value\n");
|
||||
#endif
|
||||
|
||||
printf(" ...(more to come)...\n\n");
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static void
|
||||
effaceable_hexdump(const void * buf, uint32_t count)
|
||||
{
|
||||
uint32_t i, j;
|
||||
for (i = 0; i < count; i += 16) {
|
||||
printf("0x%08x:", ((uint32_t) buf) + i);
|
||||
for (j = 0; (j < 16) && ((i + j) < count); j++) {
|
||||
printf(" %02.2x", ((uint8_t*)buf)[i+j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define UNTAG(x) \
|
||||
\
|
||||
(uint8_t)(((x) >> 24) & 0xff), \
|
||||
(uint8_t)(((x) >> 16) & 0xff), \
|
||||
(uint8_t)(((x) >> 8) & 0xff), \
|
||||
(uint8_t)((x) & 0xff)
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int
|
||||
lockerList(void)
|
||||
{
|
||||
uint8_t *buf, *cursor;
|
||||
uint32_t size;
|
||||
AppleEffaceableStorageLockerHeader *header;
|
||||
|
||||
size = effaceable_get_capacity();
|
||||
if (0 == size) {
|
||||
printf("ERROR: unable to get capacity\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = malloc(size);
|
||||
|
||||
if (!effaceable_get_bytes(buf, 0, size)) {
|
||||
printf("ERROR: unable to read\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cursor = buf;
|
||||
for (;;) {
|
||||
header = (AppleEffaceableStorageLockerHeader *)cursor;
|
||||
if (header->magic != kAppleEffaceableStorageLockerMagic) {
|
||||
printf("WARNING: unexpected magic 0x%04x at %u, expected 0x%04x\n",
|
||||
header->magic, cursor - buf, kAppleEffaceableStorageLockerMagic);
|
||||
break;
|
||||
}
|
||||
if (header->type_id == kAppleEffaceableStorageLockerSentinel) {
|
||||
printf("<END>\n");
|
||||
break;
|
||||
}
|
||||
printf("0x%08x/0x%04x %c%c%c%c %s\n",
|
||||
header->type_id,
|
||||
header->data_size,
|
||||
UNTAG(header->type_id & ~kAppleEffaceableStorageLockerProtected),
|
||||
(header->type_id & kAppleEffaceableStorageLockerProtected) ? "(protected)" : "");
|
||||
cursor += sizeof(*header);
|
||||
if (header->data_size > 0) {
|
||||
if ((cursor + header->data_size) > (buf + size)) {
|
||||
printf("WARNING: data overflows buffer\n");
|
||||
break;
|
||||
}
|
||||
effaceable_hexdump(cursor, header->data_size);
|
||||
cursor += header->data_size;
|
||||
}
|
||||
if ((cursor + sizeof(*header)) > (buf + size)) {
|
||||
printf("WARNING: header overflows buffer\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// XXX correct hard-coding of SHA1 buffer size
|
||||
#define SHA1_HASH_SIZE 20
|
||||
|
||||
static int
|
||||
consumeNonce(void)
|
||||
{
|
||||
uint64_t nonce;
|
||||
uint8_t hash[SHA1_HASH_SIZE];
|
||||
int idx;
|
||||
|
||||
if (!effaceable_consume_nonce(&nonce)) {
|
||||
printf("ERROR: unable to consume nonce\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sha1_calculate(&nonce, sizeof(nonce), hash);
|
||||
|
||||
for (idx = 0; idx < SHA1_HASH_SIZE; idx++) {
|
||||
printf("%s%02X", (idx ? " " : ""), hash[idx]);
|
||||
}
|
||||
printf(" (0x%016llX)\n", nonce);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
MENU_COMMAND_DEBUG(effaceable, do_effaceable, "effaceable storage utilities", NULL);
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <lib/effaceable.h>
|
||||
#include <lib/effaceable_impl.h>
|
||||
#include <lib/libc.h>
|
||||
|
||||
#include <effaceable_contract.h>
|
||||
#include <effaceable_debug.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int logf(void * ignored, const char * fmt, ...);
|
||||
|
||||
#define debug(...) dlogf((void*)0, __VA_ARGS__ )
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static effaceable_storage_t * gEffaceableStorage = (void*)0;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
extern void registerEffaceableStorage(effaceable_storage_t * storage)
|
||||
{
|
||||
gEffaceableStorage = storage;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
effaceable_storage_t * locateEffaceableStorage(void)
|
||||
{
|
||||
if (0 == gEffaceableStorage) {
|
||||
debug(ERR, "no storage available");
|
||||
}
|
||||
|
||||
return gEffaceableStorage;
|
||||
}
|
||||
|
||||
bool trustEffaceableContext(void)
|
||||
{
|
||||
#if DEBUG_BUILD
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
bool effaceable_get_locker(uint32_t type_id, void * data, uint32_t * data_size)
|
||||
{
|
||||
bool trust = trustEffaceableContext();
|
||||
effaceable_storage_t * storage = locateEffaceableStorage();
|
||||
|
||||
if (0 == storage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kEffaceableReturnSuccess != storage->getLocker(storage, type_id, data, data_size, !trust)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool effaceable_set_locker(uint32_t type_id, const void * data, uint32_t data_size)
|
||||
{
|
||||
bool trust = trustEffaceableContext();
|
||||
effaceable_storage_t * storage = locateEffaceableStorage();
|
||||
|
||||
if (0 == storage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kEffaceableReturnSuccess != storage->setLocker(storage, type_id, data, data_size, !trust)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool effaceable_space_for_locker(uint32_t type_id, uint32_t * data_space)
|
||||
{
|
||||
effaceable_storage_t * storage = locateEffaceableStorage();
|
||||
|
||||
if (0 == storage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kEffaceableReturnSuccess != storage->spaceForLocker(storage, type_id, data_space)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool effaceable_efface_locker(uint32_t type_id)
|
||||
{
|
||||
bool trust = trustEffaceableContext();
|
||||
effaceable_storage_t * storage = locateEffaceableStorage();
|
||||
|
||||
if (0 == storage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kEffaceableReturnSuccess != storage->effaceLocker(storage, type_id, !trust)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool effaceable_consume_nonce(uint64_t * nonce)
|
||||
{
|
||||
effaceable_storage_t * storage = locateEffaceableStorage();
|
||||
|
||||
if (0 == storage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kEffaceableReturnSuccess != storage->consumeNonce(storage, nonce)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#if DEBUG_BUILD
|
||||
|
||||
bool effaceable_get_bytes(void * client_buf, uint32_t offset, uint32_t count)
|
||||
{
|
||||
effaceable_storage_t * storage = locateEffaceableStorage();
|
||||
|
||||
if (0 == storage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (kEffaceableReturnSuccess != storage->getBytes(storage, client_buf, offset, count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t effaceable_get_capacity(void)
|
||||
{
|
||||
effaceable_storage_t * storage = locateEffaceableStorage();
|
||||
uint32_t capacity = 0;
|
||||
|
||||
if (0 != storage) {
|
||||
capacity = storage->getCapacity(storage);
|
||||
}
|
||||
|
||||
debug(INFO, "capacity is %d bytes", capacity);
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int logf(void * ignored, const char * fmt, ...)
|
||||
{
|
||||
int err;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
err = vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 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 <effaceable_contract.h>
|
||||
#include <effaceable_debug.h>
|
||||
#include <lib/effaceable_impl.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <lib/random.h>
|
||||
#include <lib/cksum.h>
|
||||
#include <lib/libc.h>
|
||||
#include <drivers/sha1.h>
|
||||
#include <sys.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#if DEBUG_BUILD
|
||||
# define USING_STRONG_SEED false
|
||||
#else
|
||||
# define USING_STRONG_SEED true
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int logf(void * ignored, const char * fmt, ...);
|
||||
|
||||
#define debug(...) dlogf((void*)0, __VA_ARGS__ )
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void * allocMemIboot(effaceable_system_t * system, uint32_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void freeMemIboot(effaceable_system_t * system, void * buf, uint32_t size)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void * setMemIboot(effaceable_system_t * system, void * buf, uint8_t val, uint32_t size)
|
||||
{
|
||||
return memset(buf, val, size);
|
||||
}
|
||||
|
||||
void * moveMemIboot(effaceable_system_t * system, void * dst, const void * src, uint32_t size)
|
||||
{
|
||||
return memmove(dst, src, size);
|
||||
}
|
||||
|
||||
int cmpMemIboot(effaceable_system_t * system, const void * lhs, const void * rhs, uint32_t size)
|
||||
{
|
||||
return memcmp(lhs, rhs, size);
|
||||
}
|
||||
|
||||
void readRandomIboot(effaceable_system_t * system, void * buf, uint32_t size)
|
||||
{
|
||||
static bool is_seeded = false;
|
||||
const unsigned increment = sizeof(int);
|
||||
uint8_t * cursor = (uint8_t *)buf;
|
||||
unsigned idx;
|
||||
|
||||
// if we're using a strong seed, draw from the entropy pool to
|
||||
// re-seed psuedo-random generator the first time function is called
|
||||
if (!is_seeded && USING_STRONG_SEED) {
|
||||
unsigned int seed;
|
||||
random_get_bytes((u_int8_t*)&seed, sizeof(seed));
|
||||
srand(seed);
|
||||
is_seeded = true;
|
||||
}
|
||||
|
||||
// fill with psuedo-random values by 'int'-sized chunks as long as possible
|
||||
for (idx = 0; (idx + increment) <= size; idx += increment, cursor += increment) {
|
||||
*((int*)cursor) = rand();
|
||||
}
|
||||
|
||||
// if size doesn't divide evenly by increment, fill fringe bytewise
|
||||
for (; idx < size; idx++, cursor++) {
|
||||
*cursor = (uint8_t)rand();
|
||||
}
|
||||
}
|
||||
|
||||
void calcSHA1Iboot(effaceable_system_t * system, const void * buf, uint32_t size, void * hash)
|
||||
{
|
||||
sha1_calculate(buf, size, hash);
|
||||
}
|
||||
|
||||
uint32_t crc32Iboot(effaceable_system_t * system, uint32_t crc, const void * buf, uint32_t size, bool finish)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
if ((0 == crc) && (0 == buf) && (0 == size)) {
|
||||
val = update_crc32(0xffffffffL, 0, 0);
|
||||
} else {
|
||||
val = update_crc32(crc, (unsigned char *)buf, size);
|
||||
}
|
||||
|
||||
return (finish ? val ^ 0xffffffffL : val);
|
||||
}
|
||||
|
||||
bool setPropertyIboot(effaceable_system_t * system, const char * key, uint32_t value)
|
||||
{
|
||||
debug(INFO, "%s := %u", key, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
void panicIboot(effaceable_system_t * system, const char * msg)
|
||||
{
|
||||
panic("%s", msg);
|
||||
}
|
||||
|
||||
int vlogfIboot(effaceable_system_t * system, const char * fmt, va_list ap)
|
||||
{
|
||||
return vprintf(fmt, ap);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
extern EffaceableReturn
|
||||
setupEffaceableSystemContract(effaceable_system_t * system)
|
||||
{
|
||||
system->opaque = 0;
|
||||
|
||||
system->allocMem = allocMemIboot;
|
||||
system->freeMem = freeMemIboot;
|
||||
system->setMem = setMemIboot;
|
||||
system->moveMem = moveMemIboot;
|
||||
system->cmpMem = cmpMemIboot;
|
||||
system->readRandom = readRandomIboot;
|
||||
system->calcSHA1 = calcSHA1Iboot;
|
||||
system->crc32 = crc32Iboot;
|
||||
system->setProperty = setPropertyIboot;
|
||||
system->panicSys = panicIboot;
|
||||
system->vlogf = vlogfIboot;
|
||||
|
||||
return kEffaceableReturnSuccess;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int logf(void * ignored, const char * fmt, ...)
|
||||
{
|
||||
int err;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
err = vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_STORAGE_API_H_
|
||||
#define _EFFACEABLE_STORAGE_API_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
// =============================================================================
|
||||
//
|
||||
// iBoot-specific presentation of the effaceable storage API for client use.
|
||||
//
|
||||
// =============================================================================
|
||||
|
||||
bool effaceable_get_locker(uint32_t type_id, void * data, uint32_t * data_size);
|
||||
bool effaceable_set_locker(uint32_t type_id, const void * data, uint32_t data_size);
|
||||
bool effaceable_space_for_locker(uint32_t type_id, uint32_t * data_space);
|
||||
bool effaceable_efface_locker(uint32_t type_id);
|
||||
bool effaceable_consume_nonce(uint64_t * nonce);
|
||||
|
||||
// =============================================================================
|
||||
//
|
||||
// iBoot-specific presentation of the effaceable storage API for util/devel.
|
||||
//
|
||||
// =============================================================================
|
||||
|
||||
bool effaceable_get_bytes(void * client_buf, uint32_t offset, uint32_t count);
|
||||
uint32_t effaceable_get_capacity(void);
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_STORAGE_API_H_ */
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_IMPL_H_
|
||||
#define _EFFACEABLE_IMPL_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <effaceable_contract.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
extern EffaceableReturn setupEffaceableSystemContract(effaceable_system_t * system);
|
||||
extern void registerEffaceableStorage(effaceable_storage_t * storage);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
__END_DECLS
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_IMPL_H_ */
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <lib/effaceable.h>
|
||||
#include <lib/effaceable_impl.h>
|
||||
|
||||
#include <effaceable_contract.h>
|
||||
#include <effaceable_debug.h>
|
||||
|
||||
#include <nand_part.h>
|
||||
#include <nand_part_interface.h>
|
||||
|
||||
#include <lib/heap.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
struct _context
|
||||
{
|
||||
NandPartInterface * npi;
|
||||
uint32_t idx;
|
||||
};
|
||||
|
||||
typedef struct _context context_t;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static inline context_t * context(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return (context_t*)nand->opaque;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define delegateNPI(_me, _fn, ...) context(_me)->npi->_fn(context(_me)->npi->core, context(_me)->idx, ## __VA_ARGS__)
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static void initContext(effaceable_nand_hal_t * nand, NandPartInterface * npi, uint32_t idx)
|
||||
{
|
||||
context_t * ctx = (context_t*)malloc(sizeof(context_t));
|
||||
|
||||
ctx->npi = npi;
|
||||
ctx->idx = idx;
|
||||
|
||||
nand->opaque = ctx;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t getPageSizeHook(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return delegateNPI(nand, get_bytes_per_page);
|
||||
}
|
||||
|
||||
static uint32_t getPageCountHook(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return delegateNPI(nand, get_pages_per_block);
|
||||
}
|
||||
|
||||
static uint32_t getBlockCountHook(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return delegateNPI(nand, get_block_depth);
|
||||
}
|
||||
|
||||
static uint32_t getBankCountHook(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return delegateNPI(nand, get_bank_width);
|
||||
}
|
||||
|
||||
static bool isBlockBadHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block)
|
||||
{
|
||||
return delegateNPI(nand, is_block_bad, bank, block);
|
||||
}
|
||||
|
||||
static EffaceableReturn eraseBlockHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block)
|
||||
{
|
||||
return (delegateNPI(nand, erase_block, bank, block) ? kEffaceableReturnSuccess : kEffaceableReturnError);
|
||||
}
|
||||
|
||||
static EffaceableReturn writePageHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length)
|
||||
{
|
||||
return (delegateNPI(nand, write_page, bank, block, page, (void*)buf) ? kEffaceableReturnSuccess : kEffaceableReturnError);
|
||||
}
|
||||
|
||||
static EffaceableReturn readPageHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length)
|
||||
{
|
||||
return (delegateNPI(nand, read_page, bank, block, page, buf, (bool*)0) ? kEffaceableReturnSuccess : kEffaceableReturnError);
|
||||
}
|
||||
|
||||
static bool requestPartitionTableDiffHook(effaceable_nand_hal_t * nand, void * buf, uint32_t length)
|
||||
{
|
||||
return delegateNPI(nand, request_ptab_diff, buf, length);
|
||||
}
|
||||
|
||||
static bool providePartitionTableDiffHook(effaceable_nand_hal_t * nand, void * buf, uint32_t length)
|
||||
{
|
||||
return delegateNPI(nand, provide_ptab_diff, buf, length);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void initContract(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
nand->getPageSize = getPageSizeHook;
|
||||
nand->getPageCount = getPageCountHook;
|
||||
nand->getBlockCount = getBlockCountHook;
|
||||
nand->getBankCount = getBankCountHook;
|
||||
nand->isBlockBad = isBlockBadHook;
|
||||
nand->eraseBlock = eraseBlockHook;
|
||||
nand->writePage = writePageHook;
|
||||
nand->readPage = readPageHook;
|
||||
nand->requestPartitionTableDiff = requestPartitionTableDiffHook;
|
||||
nand->providePartitionTableDiff = providePartitionTableDiffHook;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
extern void
|
||||
effaceable_nand_init(NandPartInterface * npi, uint32_t idx)
|
||||
{
|
||||
effaceable_nand_hal_t * nand = (effaceable_nand_hal_t *)malloc(sizeof(effaceable_nand_hal_t));
|
||||
effaceable_device_t * device = (effaceable_device_t *)malloc(sizeof(effaceable_device_t));
|
||||
effaceable_system_t * system = (effaceable_system_t *)malloc(sizeof(effaceable_system_t));
|
||||
effaceable_storage_t * storage = (effaceable_storage_t *)malloc(sizeof(effaceable_storage_t));
|
||||
|
||||
initContext(nand, npi, idx);
|
||||
initContract(nand);
|
||||
|
||||
if (kEffaceableReturnSuccess == setupEffaceableSystemContract(system)) {
|
||||
|
||||
startEffaceableNAND(device, system, nand, storage);
|
||||
registerEffaceableStorage(storage);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_NAND_H_
|
||||
#define _EFFACEABLE_NAND_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void effaceable_nand_init(NandPartInterface * npi, uint32_t idx);
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_NAND_H_ */
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
# XXX Clean this kludge up when iBoot two-stage boot changes are submitted.
|
||||
ifneq ($(PRODUCT),iBSS)
|
||||
|
||||
OPTIONS += \
|
||||
WITH_EFFACEABLE_NAND=1
|
||||
|
||||
MODULES += \
|
||||
lib/effaceable/iboot
|
||||
|
||||
# XXX Clean this up when iBoot two-stage boot changes are submitted.
|
||||
GLOBAL_INCLUDES += \
|
||||
drivers/flash_nand/boot \
|
||||
$(LOCAL_DIR)/include
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/effaceable_nand.o
|
||||
|
||||
endif
|
||||
|
||||
# XXX Consider relocating this directory to 'iBoot/drivers/apple/effaceable/nand'.
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <lib/effaceable.h>
|
||||
#include <lib/effaceable_impl.h>
|
||||
|
||||
#include <effaceable_contract.h>
|
||||
#include <effaceable_debug.h>
|
||||
|
||||
#include <sys.h>
|
||||
#include <AssertMacros.h>
|
||||
#include <lib/blockdev.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define WITH_EFFACEABLE_NOR_DEBUG 0
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int logf(void * ignored, const char * fmt, ...);
|
||||
|
||||
#define debug(...) dlogf((void*)0, __VA_ARGS__ )
|
||||
|
||||
// =============================================================================
|
||||
//
|
||||
// Effaceable NOR region definition is currently only supplied by means
|
||||
// of publishing device tree up to iOS. Currently, in order to get this off
|
||||
// its feet, I'm about to hard code equivalent information here; however,
|
||||
// that's obviously not a very good solution going forward (for any of the
|
||||
// NOR memory map, honestly).
|
||||
//
|
||||
// =============================================================================
|
||||
|
||||
#define NOR_BDEV "nor0"
|
||||
#define REGION_SIZE ((uint32_t)0x1000)
|
||||
|
||||
static const uint32_t region_offsets[] = {0xFA000, 0xFB000};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void validate(effaceable_nor_hal_t * nor, uint32_t index, uint32_t length)
|
||||
{
|
||||
require(nor != 0, fail_contract);
|
||||
require(nor->opaque != 0, fail_context);
|
||||
require(index < sizeof(region_offsets)/sizeof(region_offsets[0]), fail_index);
|
||||
require(length <= REGION_SIZE, fail_length);
|
||||
return;
|
||||
|
||||
fail_contract:
|
||||
panic("contract missing");
|
||||
|
||||
fail_context:
|
||||
panic("context missing");
|
||||
|
||||
fail_index:
|
||||
panic("bad index");
|
||||
|
||||
fail_length:
|
||||
panic("bad length");
|
||||
}
|
||||
|
||||
uint32_t getNorOffset(uint32_t index)
|
||||
{
|
||||
return region_offsets[index];
|
||||
}
|
||||
|
||||
static void
|
||||
doHexdump(const void * buf, uint32_t count)
|
||||
{
|
||||
uint32_t i, j;
|
||||
for (i = 0; i < count; i += 16) {
|
||||
printf("0x%08x:", ((uint32_t) buf) + i);
|
||||
for (j = 0; (j < 16) && ((i + j) < count); j++) {
|
||||
printf(" %2.2x", ((uint8_t*)buf)[i+j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define context(nor) ((struct blockdev *)nor->opaque)
|
||||
|
||||
// =============================================================================
|
||||
|
||||
uint32_t getRegionCountIboot(effaceable_nor_hal_t * nor)
|
||||
{
|
||||
validate(nor, 0, 0);
|
||||
return sizeof(region_offsets)/sizeof(region_offsets[0]);
|
||||
}
|
||||
|
||||
uint32_t getRegionSizeIboot(effaceable_nor_hal_t * nor, uint32_t index)
|
||||
{
|
||||
validate(nor, index, 0);
|
||||
return REGION_SIZE;
|
||||
}
|
||||
|
||||
EffaceableReturn eraseRegionIboot(effaceable_nor_hal_t * nor, uint32_t index, uint32_t length)
|
||||
{
|
||||
EffaceableReturn ok = kEffaceableReturnSuccess;
|
||||
int err;
|
||||
|
||||
validate(nor, index, length);
|
||||
|
||||
err = blockdev_erase(context(nor), getNorOffset(index), length);
|
||||
|
||||
if (err < 0) {
|
||||
ok = kEffaceableReturnIOError;
|
||||
}
|
||||
debug(INFO, "blockdev_erase on '%s' %s", NOR_BDEV, (kEffaceableReturnSuccess == ok) ? "succeeded" : "failed");
|
||||
return ok;
|
||||
}
|
||||
|
||||
uint32_t writeRegionIboot(effaceable_nor_hal_t * nor, uint32_t index, const void * buf, uint32_t length)
|
||||
{
|
||||
EffaceableReturn ok = kEffaceableReturnSuccess;
|
||||
int err;
|
||||
|
||||
validate(nor, index, length);
|
||||
|
||||
err = blockdev_write(context(nor), buf, getNorOffset(index), length);
|
||||
|
||||
if (err <= 0) {
|
||||
ok = kEffaceableReturnIOError;
|
||||
}
|
||||
debug(INFO, "blockdev_write to '%s' %s", NOR_BDEV, (kEffaceableReturnSuccess == ok) ? "succeeded" : "failed");
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool readRegionIboot(effaceable_nor_hal_t * nor, uint32_t index, void * buf, uint32_t length)
|
||||
{
|
||||
EffaceableReturn ok = kEffaceableReturnSuccess;
|
||||
int err;
|
||||
|
||||
validate(nor, index, length);
|
||||
|
||||
err = blockdev_read(context(nor), buf, getNorOffset(index), length);
|
||||
|
||||
if (err <= 0) {
|
||||
ok = kEffaceableReturnIOError;
|
||||
} else {
|
||||
#if WITH_EFFACEABLE_NOR_DEBUG
|
||||
doHexdump(buf, length);
|
||||
#endif
|
||||
}
|
||||
debug(INFO, "blockdev_read from '%s' %s", NOR_BDEV, (kEffaceableReturnSuccess == ok) ? "succeeded" : "failed");
|
||||
return kEffaceableReturnSuccess;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
EffaceableReturn
|
||||
setupEffaceableNorContract(effaceable_nor_hal_t * nor)
|
||||
{
|
||||
nor->opaque = lookup_blockdev(NOR_BDEV);
|
||||
|
||||
if (0 == nor->opaque) {
|
||||
debug(ERR, "unable to find '%s' blockdev", NOR_BDEV);
|
||||
return kEffaceableReturnBadMedia;
|
||||
}
|
||||
|
||||
nor->getRegionCount = getRegionCountIboot;
|
||||
nor->getRegionSize = getRegionSizeIboot;
|
||||
nor->eraseRegion = eraseRegionIboot;
|
||||
nor->writeRegion = writeRegionIboot;
|
||||
nor->readRegion = readRegionIboot;
|
||||
|
||||
debug(INFO, "nor contract setup complete");
|
||||
|
||||
return kEffaceableReturnSuccess;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
extern void
|
||||
effaceable_nor_init(void)
|
||||
{
|
||||
effaceable_nor_hal_t * nor = (effaceable_nor_hal_t *)malloc(sizeof(effaceable_nor_hal_t));
|
||||
effaceable_device_t * device = (effaceable_device_t *)malloc(sizeof(effaceable_device_t));
|
||||
effaceable_system_t * system = (effaceable_system_t *)malloc(sizeof(effaceable_system_t));
|
||||
effaceable_storage_t * storage = (effaceable_storage_t *)malloc(sizeof(effaceable_storage_t));
|
||||
|
||||
if ((kEffaceableReturnSuccess == setupEffaceableNorContract(nor)) &&
|
||||
(kEffaceableReturnSuccess == setupEffaceableSystemContract(system))) {
|
||||
|
||||
startEffaceableNOR(device, system, nor, storage);
|
||||
registerEffaceableStorage(storage);
|
||||
debug(INFO, "registered nor-backed effaceable storage");
|
||||
|
||||
} else {
|
||||
|
||||
debug(ERR, "failed to init nor-backed effaceable storage");
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static int logf(void * ignored, const char * fmt, ...)
|
||||
{
|
||||
int err;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
err = vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _EFFACEABLE_NOR_H_
|
||||
#define _EFFACEABLE_NOR_H_
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void effaceable_nor_init(void);
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _EFFACEABLE_NOR_H_ */
|
||||
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
# XXX Clean this kludge up when iBoot two-stage boot changes are submitted.
|
||||
ifneq ($(PRODUCT),iBSS)
|
||||
|
||||
OPTIONS += \
|
||||
WITH_EFFACEABLE_NOR=1
|
||||
|
||||
MODULES += \
|
||||
lib/effaceable/iboot
|
||||
|
||||
GLOBAL_INCLUDES += \
|
||||
$(LOCAL_DIR)/include
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/effaceable_nor.o
|
||||
|
||||
endif
|
||||
|
||||
# XXX Consider relocating this directory to 'iBoot/drivers/apple/effaceable/nand'.
|
||||
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
OPTIONS += \
|
||||
WITH_EFFACEABLE=1
|
||||
|
||||
MODULES += \
|
||||
lib/effaceable/common
|
||||
|
||||
GLOBAL_INCLUDES += \
|
||||
$(LOCAL_DIR)/include
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/effaceable_system.o \
|
||||
$(LOCAL_DIR)/effaceable_storage.o \
|
||||
$(LOCAL_DIR)/effaceable_cmd.o
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 <stdint.h>
|
||||
#include <IOKit/IOLib.h>
|
||||
|
||||
#include "AppleEffaceableNAND.h"
|
||||
|
||||
#include "effaceable_contract.h"
|
||||
#include "effaceable_debug.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define debug(...) dlogf((void*)0, __VA_ARGS__ )
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#ifdef super
|
||||
#undef super
|
||||
#endif
|
||||
#define super AppleEffaceableStorage
|
||||
|
||||
OSDefineMetaClassAndStructors(AppleEffaceableNAND, AppleEffaceableStorage);
|
||||
|
||||
// =============================================================================
|
||||
|
||||
bool
|
||||
AppleEffaceableNAND::start(IOService *provider)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
debug(INFO, "starting");
|
||||
|
||||
setupNandContract();
|
||||
|
||||
if (0 == (_partition = OSDynamicCast(IOFlashStoragePartition, provider))) {
|
||||
debug(ERR, "provider is not an IOFlashStoragePartition");
|
||||
} else if (!super::start(provider)) {
|
||||
debug(ERR, "superclass unable to start");
|
||||
} else {
|
||||
debug(INIT, "started");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
effaceable_nand_hal_t *
|
||||
AppleEffaceableNAND::nand(void)
|
||||
{
|
||||
return &_nand_hal;
|
||||
}
|
||||
|
||||
void
|
||||
AppleEffaceableNAND::setupDeviceContract(void)
|
||||
{
|
||||
startEffaceableNAND(device(), system(), nand(), storage());
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
AppleEffaceableNAND * context(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
AppleEffaceableNAND * context;
|
||||
|
||||
// XXX assert instead?
|
||||
if (0 == (context = (AppleEffaceableNAND *)nand->opaque))
|
||||
panic("null context");
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
uint32_t getPageSizeHook(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return context(nand)->getPageSizeNand();
|
||||
}
|
||||
|
||||
uint32_t getPageCountHook(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return context(nand)->getPageCountNand();
|
||||
}
|
||||
|
||||
uint32_t getBlockCountHook(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return context(nand)->getBlockCountNand();
|
||||
}
|
||||
|
||||
uint32_t getBankCountHook(effaceable_nand_hal_t * nand)
|
||||
{
|
||||
return context(nand)->getBankCountNand();
|
||||
}
|
||||
|
||||
bool isBlockBadHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block)
|
||||
{
|
||||
return context(nand)->isBlockBadNand(bank, block);
|
||||
}
|
||||
|
||||
EffaceableReturn eraseBlockHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block)
|
||||
{
|
||||
return context(nand)->eraseBlockNand(bank, block);
|
||||
}
|
||||
|
||||
EffaceableReturn writePageHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length)
|
||||
{
|
||||
return context(nand)->writePageNand(bank, block, page, buf, length);
|
||||
}
|
||||
|
||||
EffaceableReturn readPageHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length)
|
||||
{
|
||||
return context(nand)->readPageNand(bank, block, page, buf, length);
|
||||
}
|
||||
|
||||
bool requestPartitionTableDiffHook(effaceable_nand_hal_t * nand, void * buf, uint32_t length)
|
||||
{
|
||||
return context(nand)->requestPartitionTableDiffNand(buf, length);
|
||||
}
|
||||
|
||||
bool providePartitionTableDiffHook(effaceable_nand_hal_t * nand, void * buf, uint32_t length)
|
||||
{
|
||||
return context(nand)->providePartitionTableDiffNand(buf, length);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void
|
||||
AppleEffaceableNAND::setupNandContract(void)
|
||||
{
|
||||
nand()->opaque = this;
|
||||
|
||||
nand()->getPageSize = getPageSizeHook;
|
||||
nand()->getPageCount = getPageCountHook;
|
||||
nand()->getBlockCount = getBlockCountHook;
|
||||
nand()->getBankCount = getBankCountHook;
|
||||
nand()->isBlockBad = isBlockBadHook;
|
||||
nand()->eraseBlock = eraseBlockHook;
|
||||
nand()->writePage = writePageHook;
|
||||
nand()->readPage = readPageHook;
|
||||
nand()->requestPartitionTableDiff = requestPartitionTableDiffHook;
|
||||
nand()->providePartitionTableDiff = providePartitionTableDiffHook;
|
||||
}
|
||||
|
||||
uint32_t AppleEffaceableNAND::getPageSizeNand(void)
|
||||
{
|
||||
return (IOByteCount)_partition->getPageSize();
|
||||
}
|
||||
|
||||
uint32_t AppleEffaceableNAND::getPageCountNand(void)
|
||||
{
|
||||
return _partition->getPagesPerBlock();
|
||||
}
|
||||
|
||||
uint32_t AppleEffaceableNAND::getBlockCountNand(void)
|
||||
{
|
||||
return _partition->getBlockCount();
|
||||
}
|
||||
|
||||
uint32_t AppleEffaceableNAND::getBankCountNand(void)
|
||||
{
|
||||
return _partition->getBankCount();
|
||||
}
|
||||
|
||||
bool AppleEffaceableNAND::isBlockBadNand(uint32_t bank, uint32_t block)
|
||||
{
|
||||
return _partition->isBlockBad(bank, block);
|
||||
}
|
||||
|
||||
EffaceableReturn AppleEffaceableNAND::eraseBlockNand(uint32_t bank, uint32_t block)
|
||||
{
|
||||
return efReturn(_partition->eraseBlock(bank, block));
|
||||
}
|
||||
|
||||
EffaceableReturn AppleEffaceableNAND::writePageNand(uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length)
|
||||
{
|
||||
IOReturn ok;
|
||||
IOBufferMemoryDescriptor * bmd;
|
||||
|
||||
// XXX It's unfortunate that the IOBMD is necessary; think about how/where to clean it up.
|
||||
|
||||
bmd = IOBufferMemoryDescriptor::withBytes(buf, length, kIODirectionInOut, true);
|
||||
if (0 == bmd)
|
||||
panic("no memory");
|
||||
|
||||
ok = _partition->writePage(bank, block, page, bmd);
|
||||
|
||||
bmd->release();
|
||||
|
||||
return efReturn(ok);
|
||||
}
|
||||
|
||||
EffaceableReturn AppleEffaceableNAND::readPageNand(uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length)
|
||||
{
|
||||
IOReturn ok;
|
||||
IOBufferMemoryDescriptor * bmd;
|
||||
|
||||
// XXX It's unfortunate that the IOBMD is necessary; think about how/where to clean it up.
|
||||
|
||||
bmd = IOBufferMemoryDescriptor::withCapacity(length, kIODirectionInOut, true);
|
||||
if (0 == bmd)
|
||||
panic("no memory");
|
||||
|
||||
ok = _partition->readPage(bank, block, page, bmd);
|
||||
|
||||
if (kIOReturnSuccess == ok)
|
||||
bmd->readBytes(0, buf, length);
|
||||
bmd->release();
|
||||
|
||||
return efReturn(ok);
|
||||
}
|
||||
|
||||
bool AppleEffaceableNAND::requestPartitionTableDiffNand(void * buf, uint32_t length)
|
||||
{
|
||||
return _partition->requestPartitionTableDiff(buf, length);
|
||||
}
|
||||
|
||||
bool AppleEffaceableNAND::providePartitionTableDiffNand(void * buf, uint32_t length)
|
||||
{
|
||||
return _partition->providePartitionTableDiff(buf, length);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _APPLE_EFFACEABLE_NAND_H
|
||||
#define _APPLE_EFFACEABLE_NAND_H
|
||||
|
||||
#include <IOKit/IOService.h>
|
||||
#include <IOKit/IOBufferMemoryDescriptor.h>
|
||||
#include <IOKit/storage/flash/IOFlashStoragePartition.h>
|
||||
#include "AppleEffaceableStorage.h"
|
||||
|
||||
#include "effaceable_contract.h"
|
||||
|
||||
class AppleEffaceableNAND : public AppleEffaceableStorage
|
||||
{
|
||||
OSDeclareDefaultStructors(AppleEffaceableNAND);
|
||||
|
||||
public:
|
||||
virtual bool start(IOService * provider);
|
||||
|
||||
friend uint32_t getPageSizeHook(effaceable_nand_hal_t * nand);
|
||||
friend uint32_t getPageCountHook(effaceable_nand_hal_t * nand);
|
||||
friend uint32_t getBlockCountHook(effaceable_nand_hal_t * nand);
|
||||
friend uint32_t getBankCountHook(effaceable_nand_hal_t * nand);
|
||||
friend bool isBlockBadHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block);
|
||||
friend EffaceableReturn eraseBlockHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block);
|
||||
friend EffaceableReturn writePageHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length);
|
||||
friend EffaceableReturn readPageHook(effaceable_nand_hal_t * nand, uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length);
|
||||
friend bool requestPartitionTableDiffHook(effaceable_nand_hal_t * nand, void * buf, uint32_t length);
|
||||
friend bool providePartitionTableDiffHook(effaceable_nand_hal_t * nand, void * buf, uint32_t length);
|
||||
|
||||
protected:
|
||||
effaceable_nand_hal_t * nand(void);
|
||||
|
||||
virtual void setupDeviceContract(void);
|
||||
|
||||
private:
|
||||
void setupNandContract(void);
|
||||
|
||||
uint32_t getPageSizeNand(void);
|
||||
uint32_t getPageCountNand(void);
|
||||
uint32_t getBlockCountNand(void);
|
||||
uint32_t getBankCountNand(void);
|
||||
bool isBlockBadNand(uint32_t bank, uint32_t block);
|
||||
EffaceableReturn eraseBlockNand(uint32_t bank, uint32_t block);
|
||||
EffaceableReturn writePageNand(uint32_t bank, uint32_t block, uint32_t page, const void * buf, uint32_t length);
|
||||
EffaceableReturn readPageNand(uint32_t bank, uint32_t block, uint32_t page, void * buf, uint32_t length);
|
||||
bool requestPartitionTableDiffNand(void * buf, uint32_t length);
|
||||
bool providePartitionTableDiffNand(void * buf, uint32_t length);
|
||||
|
||||
IOFlashStoragePartition * _partition;
|
||||
effaceable_nand_hal_t _nand_hal;
|
||||
};
|
||||
|
||||
#endif /* _APPLE_NAND_EFFACEABLE_STORAGE_H */
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 <IOKit/IOLib.h>
|
||||
|
||||
#include "AppleEffaceableNOR.h"
|
||||
|
||||
#include "effaceable_contract.h"
|
||||
#include "effaceable_debug.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define debug(...) dlogf((void*)0, __VA_ARGS__ )
|
||||
|
||||
// =============================================================================
|
||||
|
||||
|
||||
#ifdef super
|
||||
#undef super
|
||||
#endif
|
||||
#define super AppleEffaceableStorage
|
||||
|
||||
OSDefineMetaClassAndStructors(AppleEffaceableNOR, AppleEffaceableStorage);
|
||||
|
||||
// =============================================================================
|
||||
|
||||
bool
|
||||
AppleEffaceableNOR::start(IOService *provider)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
debug(INFO, "starting");
|
||||
|
||||
setupNorContract();
|
||||
|
||||
if (_is_started) {
|
||||
debug(INFO, "already started");
|
||||
ok = true;
|
||||
} else if (0 == (_nor_flash = OSDynamicCast(AppleARMNORFlashDevice, provider))) {
|
||||
debug(ERR, "bug in kext matching");
|
||||
} else if (!super::start(provider)) {
|
||||
debug(ERR, "super failed to start");
|
||||
} else {
|
||||
debug(INIT, "started");
|
||||
_is_started = true;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
effaceable_nor_hal_t *
|
||||
AppleEffaceableNOR::nor(void)
|
||||
{
|
||||
return &_nor_hal;
|
||||
}
|
||||
|
||||
void
|
||||
AppleEffaceableNOR::setupDeviceContract(void)
|
||||
{
|
||||
startEffaceableNOR(device(), system(), nor(), storage());
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
AppleEffaceableNOR * context(effaceable_nor_hal_t * nor)
|
||||
{
|
||||
AppleEffaceableNOR * context;
|
||||
|
||||
// XXX assert instead?
|
||||
if (0 == (context = (AppleEffaceableNOR *)nor->opaque))
|
||||
panic("null context");
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
uint32_t getRegionCountHook(effaceable_nor_hal_t * nor)
|
||||
{
|
||||
return context(nor)->getRegionCountNor();
|
||||
}
|
||||
|
||||
uint32_t getRegionSizeHook(effaceable_nor_hal_t * nor, uint32_t index)
|
||||
{
|
||||
return context(nor)->getRegionSizeNor(index);
|
||||
}
|
||||
|
||||
EffaceableReturn eraseRegionHook(effaceable_nor_hal_t * nor, uint32_t index, uint32_t length)
|
||||
{
|
||||
return context(nor)->eraseRegionNor(index, length);
|
||||
}
|
||||
|
||||
uint32_t writeRegionHook(effaceable_nor_hal_t * nor, uint32_t index, const void * buf, uint32_t length)
|
||||
{
|
||||
return context(nor)->writeRegionNor(index, buf, length);
|
||||
}
|
||||
|
||||
bool readRegionHook(effaceable_nor_hal_t * nor, uint32_t index, void * buf, uint32_t length)
|
||||
{
|
||||
return context(nor)->readRegionNor(index, buf, length);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void
|
||||
AppleEffaceableNOR::setupNorContract(void)
|
||||
{
|
||||
nor()->opaque = this;
|
||||
|
||||
nor()->getRegionCount = getRegionCountHook;
|
||||
nor()->getRegionSize = getRegionSizeHook;
|
||||
nor()->eraseRegion = eraseRegionHook;
|
||||
nor()->writeRegion = writeRegionHook;
|
||||
nor()->readRegion = readRegionHook;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AppleEffaceableNOR::getRegionCountNor(void)
|
||||
{
|
||||
return _nor_flash->getRegionCount();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AppleEffaceableNOR::getRegionSizeNor(uint32_t index)
|
||||
{
|
||||
return _nor_flash->getRegionSize(index);
|
||||
}
|
||||
|
||||
EffaceableReturn
|
||||
AppleEffaceableNOR::eraseRegionNor(uint32_t index, uint32_t length)
|
||||
{
|
||||
return efReturn(_nor_flash->eraseRegion(index, 0, length));
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AppleEffaceableNOR::writeRegionNor(uint32_t index, const void * buf, uint32_t length)
|
||||
{
|
||||
return _nor_flash->writeRegion(index, (const UInt8*)buf, length);
|
||||
}
|
||||
|
||||
bool
|
||||
AppleEffaceableNOR::readRegionNor(uint32_t index, void * buf, uint32_t length)
|
||||
{
|
||||
return _nor_flash->readRegion(index, (UInt8*)buf, length);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _APPLE_EFFACEABLE_NOR_H
|
||||
#define _APPLE_EFFACEABLE_NOR_H
|
||||
|
||||
#include <IOKit/IOLib.h>
|
||||
#include <IOKit/IOService.h>
|
||||
#include <IOKit/platform/AppleARMNORFlashDevice.h>
|
||||
#include "AppleEffaceableStorage.h"
|
||||
|
||||
#include "effaceable_contract.h"
|
||||
|
||||
class AppleEffaceableNOR : public AppleEffaceableStorage
|
||||
{
|
||||
OSDeclareAbstractStructors(AppleEffaceableNOR);
|
||||
|
||||
public:
|
||||
virtual bool start(IOService * provider);
|
||||
|
||||
friend uint32_t getRegionCountHook(effaceable_nor_hal_t * nor);
|
||||
friend uint32_t getRegionSizeHook(effaceable_nor_hal_t * nor, uint32_t index);
|
||||
friend EffaceableReturn eraseRegionHook(effaceable_nor_hal_t * nor, uint32_t index, uint32_t length);
|
||||
friend uint32_t writeRegionHook(effaceable_nor_hal_t * nor, uint32_t index, const void * buf, uint32_t length);
|
||||
friend bool readRegionHook(effaceable_nor_hal_t * nor, uint32_t index, void * buf, uint32_t length);
|
||||
|
||||
protected:
|
||||
effaceable_nor_hal_t * nor(void);
|
||||
|
||||
virtual void setupDeviceContract(void);
|
||||
|
||||
private:
|
||||
void setupNorContract(void);
|
||||
|
||||
uint32_t getRegionCountNor(void);
|
||||
uint32_t getRegionSizeNor(uint32_t index);
|
||||
EffaceableReturn eraseRegionNor(uint32_t index, uint32_t length);
|
||||
uint32_t writeRegionNor(uint32_t index, const void * buf, uint32_t length);
|
||||
bool readRegionNor(uint32_t index, void * buf, uint32_t length);
|
||||
|
||||
AppleARMNORFlashDevice * _nor_flash;
|
||||
bool _is_started;
|
||||
effaceable_nor_hal_t _nor_hal;
|
||||
};
|
||||
|
||||
#endif /* _APPLE_EFFACEABLE_NOR_H */
|
||||
@@ -0,0 +1,545 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012 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 <sys/types.h>
|
||||
#include <sys/random.h>
|
||||
|
||||
#include <libkern/zlib.h>
|
||||
#include <libkern/crypto/sha1.h>
|
||||
|
||||
#include <IOKit/IOTypes.h>
|
||||
#include <IOKit/IOLib.h>
|
||||
#include <IOKit/IOUserClient.h>
|
||||
|
||||
#include "AppleEffaceableStorageKeys.h"
|
||||
#include "AppleEffaceableStorageFormat.h"
|
||||
#include "AppleEffaceableStorage.h"
|
||||
|
||||
#include "effaceable_contract.h"
|
||||
#include "effaceable_debug.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define debug(...) dlogf((void*)0, __VA_ARGS__ )
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#ifdef super
|
||||
#undef super
|
||||
#endif
|
||||
#define super IOService
|
||||
|
||||
OSDefineMetaClassAndAbstractStructors(AppleEffaceableStorage, IOService);
|
||||
|
||||
// =============================================================================
|
||||
|
||||
bool AppleEffaceableStorage::start(IOService *provider)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
debug(INFO, "starting");
|
||||
|
||||
if (!super::start(provider)) {
|
||||
debug(ERR, "super did not start\n");
|
||||
} else if (NULL == (_work_loop = IOWorkLoop::workLoop())) {
|
||||
debug(ERR, "couldn't init work loop");
|
||||
} else if (NULL == (_command_gate = IOCommandGate::commandGate(this))) {
|
||||
debug(ERR, "couldn't acquire command gate");
|
||||
} else if ((NULL == (_function_get = OSSymbol::withCString(kAppleEffaceableStorageFunctionGet))) ||
|
||||
(NULL == (_function_set = OSSymbol::withCString(kAppleEffaceableStorageFunctionSet))) ||
|
||||
(NULL == (_function_space = OSSymbol::withCString(kAppleEffaceableStorageFunctionSpace))) ||
|
||||
(NULL == (_function_efface = OSSymbol::withCString(kAppleEffaceableStorageFunctionEfface))) ||
|
||||
(NULL == (_function_gen_nonce = OSSymbol::withCString(kAppleEffaceableStorageFunctionGenerateNonce)))) {
|
||||
debug(ERR, "couldn't allocate function symbols");
|
||||
} else {
|
||||
|
||||
// add command gate to work loop
|
||||
getWorkLoop()->addEventSource(_command_gate);
|
||||
|
||||
// commandgate actions
|
||||
#define _ACTION_CAST(_x) OSMemberFunctionCast(IOCommandGate::Action, this, _x)
|
||||
_get_action = _ACTION_CAST(&AppleEffaceableStorage::getLockerGated);
|
||||
_set_action = _ACTION_CAST(&AppleEffaceableStorage::setLockerGated);
|
||||
_space_action = _ACTION_CAST(&AppleEffaceableStorage::spaceForLockerGated);
|
||||
_efface_action = _ACTION_CAST(&AppleEffaceableStorage::effaceLockerGated);
|
||||
_gen_nonce_action = _ACTION_CAST(&AppleEffaceableStorage::generateNonceGated);
|
||||
_format_action = _ACTION_CAST(&AppleEffaceableStorage::formatStorageGated);
|
||||
_get_bytes_action = _ACTION_CAST(&AppleEffaceableStorage::getBytesGated);
|
||||
_wipe_action = _ACTION_CAST(&AppleEffaceableStorage::wipeStorageGated);
|
||||
#undef _ACTION_CAST
|
||||
|
||||
// initalize portable core
|
||||
setupSystemContract();
|
||||
setupDeviceContract();
|
||||
|
||||
registerService();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
if (NULL != _command_gate) {
|
||||
_command_gate->release();
|
||||
_command_gate = NULL;
|
||||
}
|
||||
if (NULL != _work_loop) {
|
||||
_work_loop->release();
|
||||
_work_loop = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
AppleEffaceableStorage::registerService(IOOptionBits options)
|
||||
{
|
||||
const OSSymbol * user_client;
|
||||
|
||||
// hook up the user client
|
||||
user_client = OSSymbol::withCStringNoCopy("AppleEffaceableStorageUserClient");
|
||||
setProperty(gIOUserClientClassKey, (OSObject *)user_client);
|
||||
user_client->release();
|
||||
|
||||
super::registerService(options);
|
||||
}
|
||||
|
||||
IOWorkLoop *
|
||||
AppleEffaceableStorage::getWorkLoop() const
|
||||
{
|
||||
return _work_loop;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
IOByteCount
|
||||
AppleEffaceableStorage::getCapacity(void)
|
||||
{
|
||||
return (IOByteCount)storage()->getCapacity(storage());
|
||||
}
|
||||
|
||||
bool
|
||||
AppleEffaceableStorage::isFormatted(void)
|
||||
{
|
||||
return storage()->isFormatted(storage());
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::formatStorage(void)
|
||||
{
|
||||
return _command_gate->runAction(_format_action);
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::getBytes(void * client_buf, IOByteCount offset, IOByteCount count)
|
||||
{
|
||||
if (PE_i_can_has_debugger(NULL)) {
|
||||
return _command_gate->runAction(_get_bytes_action, (void *) client_buf, (void *) offset, (void *) count);
|
||||
} else {
|
||||
return kIOReturnUnsupported;
|
||||
}
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::wipeStorage(void)
|
||||
{
|
||||
if (PE_i_can_has_debugger(NULL)) {
|
||||
return _command_gate->runAction(_wipe_action);
|
||||
} else {
|
||||
return kIOReturnUnsupported;
|
||||
}
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::getLocker(UInt32 type_id, void *data, IOByteCount *data_size, bool untrusted)
|
||||
{
|
||||
return _command_gate->runAction(_get_action, (void *)type_id, data, (void *)data_size, (void *)untrusted);
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::setLocker(UInt32 type_id, const void *data, IOByteCount data_size, bool untrusted)
|
||||
{
|
||||
return _command_gate->runAction(_set_action, (void *)type_id, (void *)data, (void *)data_size, (void *)untrusted);
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::spaceForLocker(UInt32 type_id, IOByteCount *data_space)
|
||||
{
|
||||
return _command_gate->runAction(_space_action, (void *)type_id, (void *)data_space);
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::effaceLocker(UInt32 type_id, bool untrusted)
|
||||
{
|
||||
return _command_gate->runAction(_efface_action, (void *)type_id, (void *)untrusted);
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::generateNonce(void * hash)
|
||||
{
|
||||
return _command_gate->runAction(_gen_nonce_action, hash);
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::callPlatformFunction(
|
||||
const OSSymbol *functionSymbol,
|
||||
bool waitForFunction,
|
||||
void *param1,
|
||||
void *param2,
|
||||
void *param3,
|
||||
void *param4)
|
||||
{
|
||||
if (functionSymbol == _function_get)
|
||||
return getLocker((UInt32)(uintptr_t)param1, param2, (IOByteCount *)param3, (bool)param4);
|
||||
|
||||
if (functionSymbol == _function_set)
|
||||
return setLocker((UInt32)(uintptr_t)param1, param2, (IOByteCount)param3, (bool)param4);
|
||||
|
||||
if (functionSymbol == _function_space)
|
||||
return effaceLocker((UInt32)(uintptr_t)param1, (IOByteCount *)param2);
|
||||
|
||||
if (functionSymbol == _function_efface)
|
||||
return effaceLocker((UInt32)(uintptr_t)param1, (bool)param2);
|
||||
|
||||
if (functionSymbol == _function_gen_nonce)
|
||||
return generateNonce(param1);
|
||||
|
||||
return(IOService::callPlatformFunction(functionSymbol, waitForFunction, param1, param2, param3, param4));
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::formatStorageGated(void)
|
||||
{
|
||||
return ioReturn(storage()->formatStorage(storage()));
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::getBytesGated(void * client_buf, IOByteCount offset, IOByteCount count)
|
||||
{
|
||||
return ioReturn(storage()->getBytes(storage(), client_buf, offset, count));
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::wipeStorageGated(void * tmp_buf)
|
||||
{
|
||||
return ioReturn(storage()->wipeStorage(storage(), tmp_buf));
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::getLockerGated(UInt32 type_id, void *data, IOByteCount *data_size, bool untrusted)
|
||||
{
|
||||
return ioReturn(storage()->getLocker(storage(), type_id, data, (uint32_t *)data_size, untrusted));
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::setLockerGated(UInt32 type_id, const void *data, IOByteCount data_size, bool untrusted)
|
||||
{
|
||||
return ioReturn(storage()->setLocker(storage(), type_id, data, (uint32_t)data_size, untrusted));
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::spaceForLockerGated(UInt32 type_id, IOByteCount *data_space)
|
||||
{
|
||||
return ioReturn(storage()->spaceForLocker(storage(), type_id, (uint32_t *)data_space));
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::effaceLockerGated(UInt32 type_id, bool untrusted)
|
||||
{
|
||||
return ioReturn(storage()->effaceLocker(storage(), type_id, untrusted));
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::generateNonceGated(void * hash)
|
||||
{
|
||||
return ioReturn(storage()->generateNonce(storage(), hash));
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
effaceable_storage_t *
|
||||
AppleEffaceableStorage::storage(void)
|
||||
{
|
||||
return &_storage;
|
||||
}
|
||||
|
||||
effaceable_device_t *
|
||||
AppleEffaceableStorage::device(void)
|
||||
{
|
||||
return &_device;
|
||||
}
|
||||
|
||||
effaceable_system_t *
|
||||
AppleEffaceableStorage::system(void)
|
||||
{
|
||||
return &_system;
|
||||
}
|
||||
|
||||
EffaceableReturn
|
||||
AppleEffaceableStorage::efReturn(IOReturn ret)
|
||||
{
|
||||
EffaceableReturn cast = kEffaceableReturnInvalid;
|
||||
switch (ret) {
|
||||
case kIOReturnSuccess : cast = kEffaceableReturnSuccess ; break;
|
||||
case kIOReturnError : cast = kEffaceableReturnError ; break;
|
||||
case kIOReturnNotFound : cast = kEffaceableReturnNotFound ; break;
|
||||
case kIOReturnUnsupported : cast = kEffaceableReturnUnsupported ; break;
|
||||
case kIOReturnNotPrivileged : cast = kEffaceableReturnNotPrivileged ; break;
|
||||
case kIOReturnNotPermitted : cast = kEffaceableReturnNotPermitted ; break;
|
||||
case kIOReturnBadArgument : cast = kEffaceableReturnBadArgument ; break;
|
||||
case kIOReturnBadMedia : cast = kEffaceableReturnBadMedia ; break;
|
||||
case kIOReturnIOError : cast = kEffaceableReturnIOError ; break;
|
||||
case kIOReturnNoMemory : cast = kEffaceableReturnNoMemory ; break;
|
||||
case kIOReturnNoSpace : cast = kEffaceableReturnNoSpace ; break;
|
||||
case kIOReturnInternalError : cast = kEffaceableReturnInternalError ; break;
|
||||
case kIOReturnUnformattedMedia : cast = kEffaceableReturnUnformattedMedia; break;
|
||||
}
|
||||
return cast;
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorage::ioReturn(EffaceableReturn ret)
|
||||
{
|
||||
IOReturn cast = kIOReturnInvalid;
|
||||
switch (ret) {
|
||||
case kEffaceableReturnSuccess : cast = kIOReturnSuccess ; break;
|
||||
case kEffaceableReturnError : cast = kIOReturnError ; break;
|
||||
case kEffaceableReturnNotFound : cast = kIOReturnNotFound ; break;
|
||||
case kEffaceableReturnUnsupported : cast = kIOReturnUnsupported ; break;
|
||||
case kEffaceableReturnNotPrivileged : cast = kIOReturnNotPrivileged ; break;
|
||||
case kEffaceableReturnNotPermitted : cast = kIOReturnNotPermitted ; break;
|
||||
case kEffaceableReturnBadArgument : cast = kIOReturnBadArgument ; break;
|
||||
case kEffaceableReturnBadMedia : cast = kIOReturnBadMedia ; break;
|
||||
case kEffaceableReturnIOError : cast = kIOReturnIOError ; break;
|
||||
case kEffaceableReturnNoMemory : cast = kIOReturnNoMemory ; break;
|
||||
case kEffaceableReturnNoSpace : cast = kIOReturnNoSpace ; break;
|
||||
case kEffaceableReturnInternalError : cast = kIOReturnInternalError ; break;
|
||||
case kEffaceableReturnUnformattedMedia : cast = kIOReturnUnformattedMedia; break;
|
||||
}
|
||||
return cast;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
int
|
||||
AppleEffaceableStorage::logf(void * ignored, const char * fmt, ...)
|
||||
{
|
||||
int err;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
err = vlogfSys(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void
|
||||
AppleEffaceableStorage::handleBootArgs(void)
|
||||
{
|
||||
uint32_t arg_flag = 0;
|
||||
bool enable_wipe = false;
|
||||
bool enable_full_scan = false;
|
||||
|
||||
// boot-arg support to enable/disable of media wipe
|
||||
if (PE_parse_boot_argn("effaceable-enable-wipe", &arg_flag, sizeof (arg_flag)) && arg_flag) {
|
||||
enable_wipe = (0 != arg_flag);
|
||||
// XXX plumb down through to core implementation
|
||||
debug(INIT, "wipe %sabled via boot-arg", enable_wipe ? "en" : "dis");
|
||||
}
|
||||
|
||||
// boot-arg support to enable/disable full scan
|
||||
if (PE_parse_boot_argn("effaceable-enable-full-scan", &arg_flag, sizeof (arg_flag)) && arg_flag) {
|
||||
enable_full_scan = (0 != arg_flag);
|
||||
// XXX plumb down through to core implementation
|
||||
debug(INIT, "full scan %sabled via boot-arg", enable_full_scan ? "en" : "dis");
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
AppleEffaceableStorage * context(effaceable_system_t * system)
|
||||
{
|
||||
AppleEffaceableStorage * context;
|
||||
|
||||
// XXX assert instead?
|
||||
if (NULL == (context = (AppleEffaceableStorage *)system->opaque))
|
||||
panic("null context");
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void * allocMemHook(effaceable_system_t * system, uint32_t size)
|
||||
{
|
||||
return context(system)->allocMemSys(size);
|
||||
}
|
||||
|
||||
void freeMemHook(effaceable_system_t * system, void * buf, uint32_t size)
|
||||
{
|
||||
context(system)->freeMemSys(buf, size);
|
||||
}
|
||||
|
||||
void * setMemHook(effaceable_system_t * system, void * buf, uint8_t val, uint32_t size)
|
||||
{
|
||||
return context(system)->setMemSys(buf, val, size);
|
||||
}
|
||||
|
||||
void * moveMemHook(effaceable_system_t * system, void * dst, const void * src, uint32_t size)
|
||||
{
|
||||
return context(system)->moveMemSys(dst, src, size);
|
||||
}
|
||||
|
||||
int cmpMemHook(effaceable_system_t * system, const void * lhs, const void * rhs, uint32_t size)
|
||||
{
|
||||
return context(system)->cmpMemSys(lhs, rhs, size);
|
||||
}
|
||||
|
||||
void readRandomHook(effaceable_system_t * system, void * buf, uint32_t size)
|
||||
{
|
||||
context(system)->readRandomSys(buf, size);
|
||||
}
|
||||
|
||||
void calcSHA1Hook(effaceable_system_t * system, const void * buf, uint32_t size, void * hash)
|
||||
{
|
||||
return context(system)->calcSHA1Sys(buf, size, hash);
|
||||
}
|
||||
|
||||
uint32_t crc32Hook(effaceable_system_t * system, uint32_t crc, const void * buf, uint32_t size, bool ignored)
|
||||
{
|
||||
return context(system)->crc32Sys(crc, buf, size);
|
||||
}
|
||||
|
||||
bool setPropertyHook(effaceable_system_t * system, const char * key, uint32_t value)
|
||||
{
|
||||
return context(system)->setPropertySys(key, value);
|
||||
}
|
||||
|
||||
void panicHook(effaceable_system_t * system, const char * msg)
|
||||
{
|
||||
context(system)->panicSys(msg);
|
||||
}
|
||||
|
||||
int vlogfHook(effaceable_system_t * system, const char * fmt, va_list ap)
|
||||
{
|
||||
return context(system)->vlogfSys(fmt, ap);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void
|
||||
AppleEffaceableStorage::setupSystemContract(void)
|
||||
{
|
||||
// XXX convert to appropriate type of AssertMacros
|
||||
if (sizeof(uint32_t) != sizeof(IOByteCount))
|
||||
panic("(sizeof(uint32_t) != sizeof(IOByteCount))");
|
||||
|
||||
system()->opaque = this;
|
||||
|
||||
system()->allocMem = allocMemHook;
|
||||
system()->freeMem = freeMemHook;
|
||||
system()->setMem = setMemHook;
|
||||
system()->moveMem = moveMemHook;
|
||||
system()->cmpMem = cmpMemHook;
|
||||
system()->readRandom = readRandomHook;
|
||||
system()->calcSHA1 = calcSHA1Hook;
|
||||
system()->crc32 = crc32Hook;
|
||||
system()->setProperty = setPropertyHook;
|
||||
system()->panicSys = panicHook;
|
||||
system()->vlogf = vlogfHook;
|
||||
}
|
||||
|
||||
void *
|
||||
AppleEffaceableStorage::allocMemSys(uint32_t size)
|
||||
{
|
||||
void * buf = IOMalloc(size);
|
||||
|
||||
if (0 == buf) {
|
||||
panic("no memory");
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
AppleEffaceableStorage::freeMemSys(void * buf, uint32_t size)
|
||||
{
|
||||
IOFree(buf, size);
|
||||
}
|
||||
|
||||
void *
|
||||
AppleEffaceableStorage::setMemSys(void * buf, uint8_t val, uint32_t size)
|
||||
{
|
||||
return memset(buf, val, size);
|
||||
}
|
||||
|
||||
void *
|
||||
AppleEffaceableStorage::moveMemSys(void * dst, const void * src, uint32_t size)
|
||||
{
|
||||
return memmove(dst, src, size);
|
||||
}
|
||||
|
||||
int
|
||||
AppleEffaceableStorage::cmpMemSys(const void * lhs, const void * rhs, uint32_t size)
|
||||
{
|
||||
return memcmp(lhs, rhs, size);
|
||||
}
|
||||
|
||||
void
|
||||
AppleEffaceableStorage::readRandomSys(void * buf, uint32_t size)
|
||||
{
|
||||
read_random(buf, size);
|
||||
}
|
||||
|
||||
void
|
||||
AppleEffaceableStorage::calcSHA1Sys(const void * buf, uint32_t size, void * hash)
|
||||
{
|
||||
SHA1_CTX ctx;
|
||||
|
||||
SHA1Init(&ctx);
|
||||
SHA1Update(&ctx, buf, size);
|
||||
SHA1Final(hash, &ctx);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AppleEffaceableStorage::crc32Sys(uint32_t crc, const void * buf, uint32_t size)
|
||||
{
|
||||
return crc32(crc, buf, size);
|
||||
}
|
||||
|
||||
bool
|
||||
AppleEffaceableStorage::setPropertySys(const char * key, uint32_t value)
|
||||
{
|
||||
return setProperty(key, value, 32);
|
||||
}
|
||||
|
||||
void
|
||||
AppleEffaceableStorage::panicSys(const char * msg)
|
||||
{
|
||||
panic(msg);
|
||||
}
|
||||
|
||||
int
|
||||
AppleEffaceableStorage::vlogfSys(const char * fmt, va_list ap)
|
||||
{
|
||||
IOLogv(fmt, ap);
|
||||
|
||||
// XXX Given that IOLogv doesn't return character printed count,
|
||||
// as least common denominator, it's probably best to reduce vlogf
|
||||
// contract to void return value.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _APPLE_EFFACEABLE_STORAGE_H
|
||||
#define _APPLE_EFFACEABLE_STORAGE_H
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#include <IOKit/IOLib.h>
|
||||
#include <IOKit/IOService.h>
|
||||
#include <IOKit/IOCommandGate.h>
|
||||
|
||||
#include "AppleEffaceableStorageKeys.h"
|
||||
#include "effaceable_contract.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class AppleEffaceableStorage : public IOService
|
||||
{
|
||||
OSDeclareAbstractStructors(AppleEffaceableStorage);
|
||||
|
||||
public:
|
||||
/* client API */
|
||||
IOReturn getLocker(UInt32 type_id, void *data, IOByteCount *data_size, bool untrusted = false);
|
||||
IOReturn setLocker(UInt32 type_id, const void *data, IOByteCount data_size, bool untrusted = false);
|
||||
IOReturn spaceForLocker(UInt32 type_id, IOByteCount *data_space);
|
||||
IOReturn effaceLocker(UInt32 type_id, bool untrusted = false);
|
||||
IOReturn generateNonce(void * hash);
|
||||
|
||||
/* additional public functions intended for manufacturing and/or diagnostics only */
|
||||
IOReturn getBytes(void * client_buf, IOByteCount offset, IOByteCount count);
|
||||
IOReturn wipeStorage(void);
|
||||
IOByteCount getCapacity(void);
|
||||
bool isFormatted(void);
|
||||
IOReturn formatStorage(void);
|
||||
|
||||
protected:
|
||||
/* subclass SPI */
|
||||
effaceable_storage_t * storage(void);
|
||||
effaceable_device_t * device(void);
|
||||
effaceable_system_t * system(void);
|
||||
|
||||
EffaceableReturn efReturn(IOReturn ret);
|
||||
IOReturn ioReturn(EffaceableReturn ret);
|
||||
|
||||
int logf(void * ignored, const char * fmt, ...);
|
||||
|
||||
virtual void setupDeviceContract(void) = 0;
|
||||
|
||||
public:
|
||||
/* IOKit hooks */
|
||||
virtual bool start(IOService * provider);
|
||||
virtual void registerService(IOOptionBits options = 0);
|
||||
virtual IOWorkLoop * getWorkLoop() const;
|
||||
virtual IOReturn callPlatformFunction(const OSSymbol *functionSymbol, bool waitForFunction,
|
||||
void *param1, void *param2, void *param3, void *param4);
|
||||
|
||||
private:
|
||||
/* gated versions of public methods */
|
||||
IOReturn wipeStorageGated(void * tmp_buf);
|
||||
IOReturn formatStorageGated(void);
|
||||
IOReturn getBytesGated(void * client_buf, IOByteCount offset, IOByteCount count);
|
||||
IOReturn getLockerGated(UInt32 type_id, void *data, IOByteCount *data_size, bool untrusted);
|
||||
IOReturn setLockerGated(UInt32 type_id, const void *data, IOByteCount data_size, bool untrusted);
|
||||
IOReturn spaceForLockerGated(UInt32 type_id, IOByteCount *data_spage);
|
||||
IOReturn effaceLockerGated(UInt32 type_id, bool untrusted);
|
||||
IOReturn generateNonceGated(void * hash);
|
||||
|
||||
/* methods to help get portable core setup and configured */
|
||||
void setupSystemContract(void);
|
||||
void handleBootArgs(void);
|
||||
|
||||
/* implementation of portable core system services contract */
|
||||
void * allocMemSys(uint32_t size);
|
||||
void freeMemSys(void * buf, uint32_t size);
|
||||
void * setMemSys(void * buf, uint8_t val, uint32_t size);
|
||||
void * moveMemSys(void * dst, const void * src, uint32_t size);
|
||||
int cmpMemSys(const void * lhs, const void * rhs, uint32_t size);
|
||||
void readRandomSys(void * buf, uint32_t size);
|
||||
void calcSHA1Sys(const void * buf, uint32_t size, void * hash);
|
||||
uint32_t crc32Sys(uint32_t crc, const void * buf, uint32_t size);
|
||||
bool setPropertySys(const char * key, uint32_t value);
|
||||
void panicSys(const char * msg);
|
||||
int vlogfSys(const char * fmt, va_list ap);
|
||||
|
||||
private:
|
||||
/* friend C function hooks implementing trampoline back to C++ for portable core system contract */
|
||||
friend void * allocMemHook(effaceable_system_t * system, uint32_t size);
|
||||
friend void freeMemHook(effaceable_system_t * system, void * buf, uint32_t size);
|
||||
friend void * setMemHook(effaceable_system_t * system, void * buf, uint8_t val, uint32_t size);
|
||||
friend void * moveMemHook(effaceable_system_t * system, void * dst, const void * src, uint32_t size);
|
||||
friend int cmpMemHook(effaceable_system_t * system, const void * lhs, const void * rhs, uint32_t size);
|
||||
friend void readRandomHook(effaceable_system_t * system, void * buf, uint32_t size);
|
||||
friend void calcSHA1Hook(effaceable_system_t * system, const void * buf, uint32_t size, void * hash);
|
||||
friend uint32_t crc32Hook(effaceable_system_t * system, uint32_t crc, const void * buf, uint32_t size, bool ignored);
|
||||
friend bool setPropertyHook(effaceable_system_t * system, const char * key, uint32_t value);
|
||||
friend void panicHook(effaceable_system_t * system, const char * msg);
|
||||
friend int vlogfHook(effaceable_system_t * system, const char * fmt, va_list ap);
|
||||
|
||||
private:
|
||||
/* implementation data */
|
||||
IOCommandGate * _command_gate;
|
||||
IOWorkLoop * _work_loop;
|
||||
const OSSymbol * _function_get;
|
||||
const OSSymbol * _function_set;
|
||||
const OSSymbol * _function_space;
|
||||
const OSSymbol * _function_efface;
|
||||
const OSSymbol * _function_gen_nonce;
|
||||
IOCommandGate::Action _get_action;
|
||||
IOCommandGate::Action _set_action;
|
||||
IOCommandGate::Action _space_action;
|
||||
IOCommandGate::Action _efface_action;
|
||||
IOCommandGate::Action _gen_nonce_action;
|
||||
IOCommandGate::Action _format_action;
|
||||
IOCommandGate::Action _wipe_action;
|
||||
IOCommandGate::Action _get_bytes_action;
|
||||
effaceable_storage_t _storage;
|
||||
effaceable_system_t _system;
|
||||
effaceable_device_t _device;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#endif /* _APPLE_EFFACEABLE_STORAGE_H */
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2012 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 <IOKit/IOTypes.h>
|
||||
#include <IOKit/IOLib.h>
|
||||
#include <IOKit/IOUserClient.h>
|
||||
|
||||
#include "AppleEffaceableStorage.h"
|
||||
#include "AppleEffaceableStorageKeys.h"
|
||||
#include "AppleEffaceableStorageUserClient.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// XXX correct hard-coding of SHA1 buffer size
|
||||
#define SHA1_HASH_SIZE 20
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_INIT (1<<0)
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_ERR (1<<1)
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_WARN (1<<2)
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_INFO (1<<3)
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_SPEW (1<<4)
|
||||
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGTAG_INIT "INIT"
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGTAG_ERR "ERROR"
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGTAG_WARN "WARNING"
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGTAG_INFO "INFO"
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DBGTAG_SPEW "SPEW"
|
||||
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DEBUG_ALL (APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_INIT | \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_ERR | \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_WARN | \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_INFO | \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_SPEW)
|
||||
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DEBUG_MOST (APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_INIT | \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_ERR | \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_WARN | \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_INFO)
|
||||
|
||||
#define APPLE_EFFACEABLE_USER_CLIENT_DEBUG_DEFAULT (APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_INIT | \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_ERR)
|
||||
|
||||
#ifdef DEBUG_BUILD
|
||||
# define APPLE_EFFACEABLE_USER_CLIENT_DEBUG APPLE_EFFACEABLE_USER_CLIENT_DEBUG_MOST
|
||||
#else
|
||||
# define APPLE_EFFACEABLE_USER_CLIENT_DEBUG APPLE_EFFACEABLE_USER_CLIENT_DEBUG_DEFAULT
|
||||
#endif
|
||||
|
||||
#if defined(APPLE_EFFACEABLE_USER_CLIENT_DEBUG) && APPLE_EFFACEABLE_USER_CLIENT_DEBUG
|
||||
# define debug(fac, fmt, args...) \
|
||||
do { \
|
||||
if (APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_##fac & \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DEBUG) { \
|
||||
kprintf("AppleEffaceableStorageUserClient::%s():[%s] " fmt "\n", \
|
||||
__FUNCTION__, \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGTAG_##fac , \
|
||||
##args ); \
|
||||
} \
|
||||
} while(0)
|
||||
# define hexdump(fac, bytes, count) \
|
||||
do { \
|
||||
if (APPLE_EFFACEABLE_USER_CLIENT_DBGMSK_##fac & \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DEBUG) { \
|
||||
uint32_t i, j; \
|
||||
for (i = 0; i < count; i += 16) { \
|
||||
kprintf("AppleEffaceableStorageUserClient::%s():[%s] 0x%08x:", \
|
||||
__FUNCTION__, \
|
||||
APPLE_EFFACEABLE_USER_CLIENT_DBGTAG_##fac , \
|
||||
((uint32_t) bytes) + i); \
|
||||
for (j = 0; (j < 16) && ((i + j) < count); j++) \
|
||||
kprintf(" %2.2x", ((uint8_t*)bytes)[i+j]); \
|
||||
kprintf("\n"); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
# define debug(fac, fmt, args...) do { /* nothing */ } while(0)
|
||||
# define hexdump(fac, bytes, count) do { /* nothing */ } while(0)
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#ifdef super
|
||||
#undef super
|
||||
#endif
|
||||
#define super IOUserClient
|
||||
|
||||
OSDefineMetaClassAndStructors(AppleEffaceableStorageUserClient, IOUserClient);
|
||||
|
||||
// =============================================================================
|
||||
|
||||
bool
|
||||
AppleEffaceableStorageUserClient::start(IOService *provider)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
_provider = OSDynamicCast(AppleEffaceableStorage, provider);
|
||||
|
||||
if (NULL == _provider) {
|
||||
debug(ERR, "bug in kext matching");
|
||||
} else if (!super::start(provider)) {
|
||||
debug(ERR, "super failed to start");
|
||||
} else {
|
||||
debug(INFO, "started");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorageUserClient::clientClose(void)
|
||||
{
|
||||
terminate();
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn
|
||||
AppleEffaceableStorageUserClient::externalMethod(uint32_t selector,
|
||||
IOExternalMethodArguments * arguments,
|
||||
IOExternalMethodDispatch *dispatch,
|
||||
OSObject *target,
|
||||
void *reference)
|
||||
{
|
||||
IOReturn ok = kIOReturnSuccess;
|
||||
bool secure_root;
|
||||
|
||||
// require administrator privilege
|
||||
if (kIOReturnSuccess != clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator)) {
|
||||
debug(ERR, "not authorized");
|
||||
return kIOReturnNotPrivileged;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_BUILD
|
||||
secure_root = true;
|
||||
#else
|
||||
// some operations require a secure root
|
||||
if (kIOReturnSuccess != (ok = _provider->getProvider()->callPlatformFunction("SecureRoot",
|
||||
false,
|
||||
_provider->getProvider(),
|
||||
&secure_root,
|
||||
0, 0))) {
|
||||
debug(ERR, "failed to determine root trust state");
|
||||
secure_root = false;
|
||||
}
|
||||
#endif // DEBUG_BUILD
|
||||
|
||||
switch (selector) {
|
||||
case kAppleEffaceableStorageMethodGetCapacity:
|
||||
|
||||
if (arguments->scalarOutputCount != 1) {
|
||||
debug(ERR, "getCapacity: missing output argument");
|
||||
ok = kIOReturnBadArgument;
|
||||
} else {
|
||||
arguments->scalarOutput[0] = _provider->getCapacity();
|
||||
}
|
||||
break;
|
||||
|
||||
case kAppleEffaceableStorageMethodIsFormatted:
|
||||
|
||||
if (arguments->scalarOutputCount != 1) {
|
||||
debug(ERR, "isFormatted: missing output argument");
|
||||
ok = kIOReturnBadArgument;
|
||||
} else {
|
||||
arguments->scalarOutput[0] = _provider->isFormatted();
|
||||
}
|
||||
break;
|
||||
|
||||
case kAppleEffaceableStorageMethodFormatStorage:
|
||||
|
||||
if (!secure_root) {
|
||||
debug(ERR, "format attempt from untrusted root");
|
||||
} else {
|
||||
ok = _provider->formatStorage();
|
||||
}
|
||||
break;
|
||||
|
||||
case kAppleEffaceableStorageMethodWipeStorage:
|
||||
|
||||
if (!secure_root) {
|
||||
debug(ERR, "wipe attempt from untrusted root");
|
||||
} else {
|
||||
ok = _provider->wipeStorage();
|
||||
}
|
||||
break;
|
||||
|
||||
case kAppleEffaceableStorageMethodGetLocker:
|
||||
|
||||
if ((arguments->scalarInputCount != 1) ||
|
||||
(arguments->scalarOutputCount != 1)) {
|
||||
debug(ERR, "getLocker: missing input/output arguments");
|
||||
ok = kIOReturnBadArgument;
|
||||
} else {
|
||||
IOByteCount data_size = arguments->structureOutputSize;
|
||||
ok = _provider->getLocker(arguments->scalarInput[0],
|
||||
arguments->structureOutput,
|
||||
&data_size,
|
||||
!secure_root);
|
||||
arguments->scalarOutput[0] = data_size;
|
||||
// if we didn't clip the data, report the actual output size
|
||||
if (data_size < arguments->structureOutputSize)
|
||||
arguments->structureOutputSize = data_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case kAppleEffaceableStorageMethodSetLocker:
|
||||
|
||||
if ((arguments->scalarInputCount != 1) ||
|
||||
(arguments->structureInputSize < 1)) {
|
||||
debug(ERR, "setLocker: missing input arguments");
|
||||
ok = kIOReturnBadArgument;
|
||||
} else {
|
||||
ok = _provider->setLocker(arguments->scalarInput[0],
|
||||
arguments->structureInput,
|
||||
arguments->structureInputSize,
|
||||
!secure_root);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAppleEffaceableStorageMethodSpace:
|
||||
|
||||
if ((arguments->scalarInputCount != 1) ||
|
||||
(arguments->scalarOutputCount != 1)) {
|
||||
debug(ERR, "lockerSpace: missing input/output arguments");
|
||||
ok = kIOReturnBadArgument;
|
||||
} else {
|
||||
IOByteCount data_space;
|
||||
ok = _provider->spaceForLocker(arguments->scalarInput[0],
|
||||
&data_space);
|
||||
arguments->scalarOutput[0] = data_space;
|
||||
}
|
||||
break;
|
||||
|
||||
case kAppleEffaceableStorageMethodEffaceLocker:
|
||||
|
||||
if (arguments->scalarInputCount != 1) {
|
||||
debug(ERR, "effaceLocker: missing input argument");
|
||||
ok = kIOReturnBadArgument;
|
||||
} else if (!secure_root && arguments->scalarInput[0] == kAppleEffaceableStorageLockerWildcard) {
|
||||
debug(ERR, "effaceLocker: untrusted attempt to efface by wildcard");
|
||||
ok = kIOReturnNotPermitted;
|
||||
} else {
|
||||
ok = _provider->effaceLocker(arguments->scalarInput[0], !secure_root);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAppleEffaceableStorageMethodGenerateNonce:
|
||||
|
||||
if (arguments->structureOutputSize != SHA1_HASH_SIZE) {
|
||||
debug(ERR, "generateNonce: missing output argument");
|
||||
ok = kIOReturnBadArgument;
|
||||
} else {
|
||||
ok = _provider->generateNonce(arguments->structureOutput);
|
||||
if (kIOReturnSuccess != ok) {
|
||||
arguments->structureOutputSize = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
//
|
||||
// Methods below are not for production use and may only be used when kernel debug enabled via boot-args.
|
||||
//
|
||||
|
||||
case kAppleEffaceableStorageMethodGetBytes:
|
||||
|
||||
if ((arguments->scalarInputCount != 1) ||
|
||||
(arguments->structureOutputSize < 1)) {
|
||||
ok = kIOReturnBadArgument;
|
||||
} else if (!PE_i_can_has_debugger(NULL)) {
|
||||
debug(ERR, "getBytes is only allowed when kernel debug is enabled");
|
||||
ok = kIOReturnUnsupported;
|
||||
} else {
|
||||
ok = _provider->getBytes(arguments->structureOutput, arguments->scalarInput[0], arguments->structureOutputSize);
|
||||
if (kIOReturnSuccess != ok) {
|
||||
debug(ERR, "getBytes from user client to service failed");
|
||||
} else {
|
||||
debug(SPEW, "read %u bytes from offset %llu", arguments->structureOutputSize, arguments->scalarInput[0]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
debug(ERR, "bad selector %u", selector);
|
||||
ok = kIOReturnUnsupported;
|
||||
break;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-11 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 _APPLE_EFFACEABLE_USER_CLIENT_H
|
||||
#define _APPLE_EFFACEABLE_USER_CLIENT_H
|
||||
|
||||
#include <IOKit/IOUserClient.h>
|
||||
#include "AppleEffaceableStorage.h"
|
||||
|
||||
class AppleEffaceableStorageUserClient : public IOUserClient
|
||||
{
|
||||
OSDeclareAbstractStructors(AppleEffaceableStorageUserClient);
|
||||
|
||||
public:
|
||||
virtual bool start(IOService * provider);
|
||||
virtual IOReturn clientClose(void);
|
||||
virtual IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments * arguments,
|
||||
IOExternalMethodDispatch * dispatch, OSObject * target,
|
||||
void * reference);
|
||||
|
||||
private:
|
||||
AppleEffaceableStorage * _provider;
|
||||
};
|
||||
|
||||
#endif /* _APPLE_EFFACEABLE_USER_CLIENT_H */
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.driver.AppleEffaceableStorage</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>KEXT</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>OSBundleCompatibleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>IOKitPersonalities</key>
|
||||
<dict>
|
||||
<key>AppleEffaceableNAND</key>
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.driver.AppleEffaceableStorage</string>
|
||||
<key>IOClass</key>
|
||||
<string>AppleEffaceableNAND</string>
|
||||
<key>IOPropertyMatch</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Content Hint</key>
|
||||
<string>Effaceable</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>IOProviderClass</key>
|
||||
<string>IOFlashMedia</string>
|
||||
</dict>
|
||||
<key>AppleEffaceableNOR</key>
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.driver.AppleEffaceableStorage</string>
|
||||
<key>IOClass</key>
|
||||
<string>AppleEffaceableNOR</string>
|
||||
<key>IONameMatch</key>
|
||||
<string>effaceable,nor</string>
|
||||
<key>IOProviderClass</key>
|
||||
<string>AppleARMNORFlashDevice</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OSBundleCompatibleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<key>OSBundleLibraries</key>
|
||||
<dict>
|
||||
<key>com.apple.driver.AppleARMPlatform</key>
|
||||
<string>1.0.0</string>
|
||||
<key>com.apple.iokit.IOFlashStorage</key>
|
||||
<string>1.0.0</string>
|
||||
<key>com.apple.kpi.bsd</key>
|
||||
<string>8.0</string>
|
||||
<key>com.apple.kpi.iokit</key>
|
||||
<string>8.0</string>
|
||||
<key>com.apple.kpi.libkern</key>
|
||||
<string>8.0</string>
|
||||
<key>com.apple.kpi.mach</key>
|
||||
<string>8.0</string>
|
||||
<key>com.apple.kpi.unsupported</key>
|
||||
<string>8.0</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
* Copyright (c) 2010 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 <stdlib.h>
|
||||
#include <err.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/random.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
|
||||
#include "AppleEffaceableStorageKeys.h"
|
||||
#include "AppleEffaceableStorageFormat.h"
|
||||
#include "effaceable.h"
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// XXX correct hard-coding of SHA1 buffer size
|
||||
#define SHA1_HASH_SIZE 20
|
||||
|
||||
// =============================================================================
|
||||
|
||||
bool
|
||||
connectClient(const char * name, io_service_t * service, io_connect_t * connection)
|
||||
{
|
||||
bool ok = false;
|
||||
mach_port_t master;
|
||||
CFDictionaryRef match;
|
||||
io_service_t found_service;
|
||||
io_connect_t found_connection;
|
||||
|
||||
if (KERN_SUCCESS != IOMasterPort(MACH_PORT_NULL, &master)) {
|
||||
warnx("IOMasterPort failed");
|
||||
} else if (IO_OBJECT_NULL == (match = IOServiceMatching(name))) {
|
||||
warnx("IOServiceMatching failed for %s", name);
|
||||
} else if (IO_OBJECT_NULL == (found_service = IOServiceGetMatchingService(master, match))) {
|
||||
warnx("IOServiceGetMatchingService failed");
|
||||
} else if (KERN_SUCCESS != IOServiceOpen(found_service, mach_task_self(), 0, &found_connection)) {
|
||||
warnx("IOServiceOpen failed for class '%s'", name);
|
||||
} else {
|
||||
*service = found_service;
|
||||
*connection = found_connection;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
// XXX clean up
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
wipeStorage(io_connect_t connection)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
if (KERN_SUCCESS != IOConnectCallScalarMethod(connection,
|
||||
kAppleEffaceableStorageMethodWipeStorage,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL)) {
|
||||
errx(1, "wipe failed");
|
||||
} else {
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
getCapacity(io_connect_t connection, IOByteCount * capacity)
|
||||
{
|
||||
bool ok = false;
|
||||
const uint32_t expected_count = 1;
|
||||
uint32_t output_count = expected_count;
|
||||
uint64_t output[output_count];
|
||||
|
||||
if (KERN_SUCCESS != IOConnectCallScalarMethod(connection,
|
||||
kAppleEffaceableStorageMethodGetCapacity,
|
||||
NULL,
|
||||
0,
|
||||
output,
|
||||
&output_count)) {
|
||||
warnx("IOConnectCallScalarMethod failed");
|
||||
} else if (expected_count != output_count) {
|
||||
warnx("unexpected scalar output count");
|
||||
} else {
|
||||
*capacity = (IOByteCount) output[0];
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
getBytes(io_connect_t connection, void * buf, IOByteCount offset, IOByteCount count)
|
||||
{
|
||||
bool ok = false;
|
||||
uint64_t input;
|
||||
size_t output_count = count;
|
||||
|
||||
input = (uint64_t)offset;
|
||||
|
||||
if (KERN_SUCCESS != IOConnectCallMethod(connection,
|
||||
kAppleEffaceableStorageMethodGetBytes,
|
||||
&input,
|
||||
1,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
buf,
|
||||
&output_count)) {
|
||||
warnx("IOConnectCallMethod failed");
|
||||
} else if (count != output_count) {
|
||||
warnx("unexpected output struct count");
|
||||
} else {
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
setBytes(io_connect_t connection, const void * buf, IOByteCount offset, IOByteCount count)
|
||||
{
|
||||
bool ok = false;
|
||||
uint64_t input;
|
||||
|
||||
input = (uint64_t)offset;
|
||||
|
||||
if (KERN_SUCCESS != IOConnectCallMethod(connection,
|
||||
kAppleEffaceableStorageMethodSetBytes,
|
||||
&input,
|
||||
1,
|
||||
buf,
|
||||
count,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL)) {
|
||||
warnx("IOConnectCallMethod failed");
|
||||
} else {
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
isFormatted(io_connect_t connection, bool * is_formatted)
|
||||
{
|
||||
bool ok = false;
|
||||
const uint32_t expected_count = 1;
|
||||
uint32_t output_count = expected_count;
|
||||
uint64_t output[output_count];
|
||||
|
||||
if (KERN_SUCCESS != IOConnectCallScalarMethod(connection,
|
||||
kAppleEffaceableStorageMethodIsFormatted,
|
||||
NULL,
|
||||
0,
|
||||
output,
|
||||
&output_count)) {
|
||||
warnx("IOConnectCallScalarMethod failed");
|
||||
} else if (expected_count != output_count) {
|
||||
warnx("unexpected scalar output count");
|
||||
} else {
|
||||
*is_formatted = (bool) output[0];
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
formatStorage(io_connect_t connection)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
if (KERN_SUCCESS != IOConnectCallScalarMethod(connection,
|
||||
kAppleEffaceableStorageMethodFormatStorage,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL)) {
|
||||
warnx("IOConnectCallScalarMethod failed");
|
||||
} else {
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
io_service_t service;
|
||||
io_connect_t connection;
|
||||
|
||||
if (0 != getuid()) {
|
||||
errx(1, "must be run as root (uid = %d)", getuid());
|
||||
}
|
||||
|
||||
if (!connectClient(kAppleEffaceableStorageClassName, &service, &connection)) {
|
||||
errx(1, "unable to connect to service");
|
||||
}
|
||||
|
||||
// crude command parser...really only intended as test vehicle
|
||||
if (1 >= argc) {
|
||||
printUsage(argc, argv);
|
||||
} else {
|
||||
if (0 == strcmp("format", argv[1])) {
|
||||
if (formatStorage(connection)) {
|
||||
printf("format succeeded\n");
|
||||
} else {
|
||||
printf("format failed\n");
|
||||
}
|
||||
} else if (0 == strcmp("wipe", argv[1])) {
|
||||
wipeStorage(connection);
|
||||
} else if (0 == strcmp("test", argv[1])) {
|
||||
quickTest(connection);
|
||||
} else if (0 == strcmp("lockers", argv[1])) {
|
||||
lockerList(connection);
|
||||
} else if (0 == strcmp("nonce", argv[1])) {
|
||||
generateNonce(connection);
|
||||
} else if (2 >= argc) {
|
||||
printUsage(argc, argv);
|
||||
} else if (0 == strcmp("efface", argv[1])) {
|
||||
lockerEfface(connection, argv[2]);
|
||||
} else if (3 >= argc) {
|
||||
printUsage(argc, argv);
|
||||
} else if (0 == strcmp("set", argv[1])) {
|
||||
lockerSet(connection, argv[2], argv[3]);
|
||||
} else if (0 == strcmp("get", argv[1])) {
|
||||
lockerGet(connection, argv[2], argv[3]);
|
||||
} else {
|
||||
printUsage(argc, argv);
|
||||
errx(1, "unrecognized subcommand");
|
||||
}
|
||||
}
|
||||
|
||||
// XXX clean up service and connection
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
printUsage(int argc, char *argv[])
|
||||
{
|
||||
printf("USAGE: %s <subcmd>\n\n", ((0 < argc) ? argv[0] : "?"));
|
||||
printf(" Where <subcmd> is one of following\n\n");
|
||||
printf(" format - format storage\n");
|
||||
printf(" wipe - wipe storage\n");
|
||||
printf(" test - perform quick test\n");
|
||||
printf(" lockers - list lockers and contents\n");
|
||||
printf(" nonce - generate a nonce and report its SHA1 hash\n");
|
||||
printf(" set <tag> <file> - write <file> into locker <tag>\n");
|
||||
printf(" get <tag> <file> - write <file> from locker <tag>\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void
|
||||
hexdump(const void * buf, IOByteCount count)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < count; i += 16) {
|
||||
printf("0x%08x:", ((unsigned) buf) + i);
|
||||
for (j = 0; (j < 16) && ((i + j) < count); j++) {
|
||||
printf(" %2.2x", ((uint8_t*)buf)[i+j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
quickTest(io_connect_t connection)
|
||||
{
|
||||
const IOByteCount count = 64;
|
||||
IOByteCount offset;
|
||||
IOByteCount capacity;
|
||||
void * sbuf = NULL;
|
||||
void * gbuf = NULL;
|
||||
unsigned i;
|
||||
|
||||
if (!getCapacity(connection, &capacity)) {
|
||||
errx(1, "unable to get capacity");
|
||||
}
|
||||
printf("capacity reported as %u; expected 960\n", (unsigned) capacity);
|
||||
|
||||
offset = capacity - count - 1;
|
||||
sbuf = malloc(count);
|
||||
gbuf = malloc(count);
|
||||
|
||||
if ((NULL == sbuf) || (NULL == gbuf)) {
|
||||
errx(1, "failed to allocate buffers");
|
||||
}
|
||||
|
||||
if (!getBytes(connection, gbuf, offset, count)) {
|
||||
errx(1, "unable to get bytes");
|
||||
}
|
||||
printf("read %u bytes from %u offset\n", (unsigned) count, (unsigned) offset);
|
||||
hexdump(gbuf, count);
|
||||
|
||||
srandom(((unsigned *)gbuf)[0]);
|
||||
for (i = 0; i < (count / sizeof(long)); i++) {
|
||||
((long *)sbuf)[i] = random();
|
||||
}
|
||||
|
||||
if (!setBytes(connection, sbuf, offset, count)) {
|
||||
errx(1, "unable to set bytes");
|
||||
}
|
||||
printf("wrote %u bytes to %u offset\n", (unsigned) count, (unsigned) offset);
|
||||
hexdump(sbuf, count);
|
||||
|
||||
if (!getBytes(connection, gbuf, offset, count)) {
|
||||
errx(1, "unable to get bytes");
|
||||
}
|
||||
printf("read %u bytes from %u offset\n", (unsigned) count, (unsigned) offset);
|
||||
hexdump(gbuf, count);
|
||||
|
||||
if (0 != bcmp(sbuf, gbuf, count)) {
|
||||
errx(1, "bytes read back differ from those written");
|
||||
}
|
||||
printf("test passed\n");
|
||||
|
||||
if (NULL != sbuf) {
|
||||
free(sbuf);
|
||||
}
|
||||
if (NULL != gbuf) {
|
||||
free(gbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
#define UNTAG(x) (int)(((x) >> 24) & 0xff),(int)(((x) >> 16) & 0xff),(int)(((x) >> 8) & 0xff),(int)((x) & 0xff)
|
||||
|
||||
void
|
||||
lockerList(io_connect_t connection)
|
||||
{
|
||||
uint8_t *buf, *cursor;
|
||||
IOByteCount size;
|
||||
AppleEffaceableStorageLockerHeader *header;
|
||||
|
||||
if (!getCapacity(connection, &size))
|
||||
errx(1, "unable to get capacity");
|
||||
if (size != kAppleEffaceableStorageLockerSize)
|
||||
warnx("size %lu not %lu as expected", size, kAppleEffaceableStorageLockerSize);
|
||||
buf = malloc(size);
|
||||
|
||||
if (!getBytes(connection, buf, 0, size))
|
||||
errx(1, "unable to read");
|
||||
|
||||
cursor = buf;
|
||||
for (;;) {
|
||||
header = (AppleEffaceableStorageLockerHeader *)cursor;
|
||||
if (header->magic != kAppleEffaceableStorageLockerMagic) {
|
||||
warnx("unexpected magic 0x%04x at %u, expected 0x%04x", header->magic, cursor - buf, kAppleEffaceableStorageLockerMagic);
|
||||
break;
|
||||
}
|
||||
if (header->type_id == kAppleEffaceableStorageLockerSentinel) {
|
||||
printf("<END>\n");
|
||||
break;
|
||||
}
|
||||
printf("0x%08x/0x%04x %c%c%c%c %s\n",
|
||||
(unsigned)header->type_id, header->data_size, UNTAG(header->type_id),
|
||||
(header->type_id & kAppleEffaceableStorageLockerProtected) ? "(protected)" : "");
|
||||
cursor += sizeof(*header);
|
||||
if (header->data_size > 0) {
|
||||
if ((cursor + header->data_size) > (buf + size)) {
|
||||
warnx("data overflows buffer");
|
||||
break;
|
||||
}
|
||||
hexdump(cursor, header->data_size);
|
||||
cursor += header->data_size;
|
||||
}
|
||||
if ((cursor + sizeof(*header)) > (buf + size)) {
|
||||
warnx("header overflows buffer");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
lockerGet(io_connect_t connection, char *tag, char *file)
|
||||
{
|
||||
UInt32 tagVal;
|
||||
UInt8 tagStr[4];
|
||||
UInt8 buf[1024];
|
||||
UInt64 input, output;
|
||||
uint32_t output_count;
|
||||
UInt32 output_size;
|
||||
int fd;
|
||||
|
||||
// get the tag
|
||||
memset(tagStr, 0, 4);
|
||||
strncpy((char *)tagStr, tag, 4);
|
||||
tagVal = ((UInt32)tagStr[0] << 24) + ((UInt32)tagStr[1] << 16) + ((UInt32)tagStr[2] << 8) + (UInt32)tagStr[3];
|
||||
|
||||
// get the locker
|
||||
input = tagVal;
|
||||
output_count = 1;
|
||||
output_size = sizeof(buf);
|
||||
if (KERN_SUCCESS != IOConnectCallMethod(connection, // connection
|
||||
kAppleEffaceableStorageMethodGetLocker, // selector
|
||||
&input, // input
|
||||
1, // input count
|
||||
NULL, // input struct
|
||||
0, // input struct size
|
||||
&output, // output
|
||||
&output_count, // output count
|
||||
&buf, // output struct
|
||||
&output_size)) { // output struct size
|
||||
errx(1, "getLocker call failed");
|
||||
}
|
||||
|
||||
// write the file
|
||||
if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0666)) < 0)
|
||||
err(1, "failed to create %s", file);
|
||||
warnx("using descriptor %d", fd);
|
||||
if (write(fd, buf, output_size) != output_size)
|
||||
err(1, "failed writing %lu bytes to %s (fd %d)", output_size, file, fd);
|
||||
close(fd);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
lockerSet(io_connect_t connection, char *tag, char *file)
|
||||
{
|
||||
UInt32 tagVal;
|
||||
UInt8 tagStr[4];
|
||||
UInt8 buf[1024];
|
||||
UInt64 input;
|
||||
int fd, size;
|
||||
|
||||
// get the tag
|
||||
memset(tagStr, 0, 4);
|
||||
strncpy((char *)tagStr, tag, 4);
|
||||
tagVal = ((UInt32)tagStr[0] << 24) + ((UInt32)tagStr[1] << 16) + ((UInt32)tagStr[2] << 8) + (UInt32)tagStr[3];
|
||||
|
||||
// read the file
|
||||
if ((fd = open(file, O_RDONLY)) < 0)
|
||||
err(1, "failed to open %s", file);
|
||||
if ((size = read(fd, buf, sizeof(buf))) < 0)
|
||||
err(1, "failed to read from %s", file);
|
||||
close(fd);
|
||||
|
||||
|
||||
// make the call
|
||||
input = tagVal;
|
||||
if (KERN_SUCCESS != IOConnectCallMethod(connection,
|
||||
kAppleEffaceableStorageMethodSetLocker,
|
||||
&input,
|
||||
1,
|
||||
&buf,
|
||||
size,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL)) {
|
||||
errx(1, "setLocker call failed");
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
lockerEfface(io_connect_t connection, char *tag)
|
||||
{
|
||||
UInt32 tagVal;
|
||||
UInt8 tagStr[4];
|
||||
UInt64 input;
|
||||
|
||||
// get the tag
|
||||
memset(tagStr, 0, 4);
|
||||
strncpy((char *)tagStr, tag, 4);
|
||||
tagVal = ((UInt32)tagStr[0] << 24) + ((UInt32)tagStr[1] << 16) + ((UInt32)tagStr[2] << 8) + (UInt32)tagStr[3];
|
||||
|
||||
// make the call
|
||||
input = tagVal;
|
||||
if (KERN_SUCCESS != IOConnectCallMethod(connection,
|
||||
kAppleEffaceableStorageMethodEffaceLocker,
|
||||
&input,
|
||||
1,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL)) {
|
||||
errx(1, "effaceLocker call failed");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
generateNonce(io_connect_t connection)
|
||||
{
|
||||
|
||||
UInt8 buf[SHA1_HASH_SIZE];
|
||||
UInt32 output_size = sizeof(buf);
|
||||
UInt32 idx;
|
||||
|
||||
memset(buf, 0, output_size);
|
||||
if (KERN_SUCCESS != IOConnectCallMethod(connection, // connection
|
||||
kAppleEffaceableStorageMethodGenerateNonce, // selector
|
||||
NULL, // input
|
||||
0, // input count
|
||||
NULL, // input struct
|
||||
0, // input struct size
|
||||
NULL, // output
|
||||
NULL, // output count
|
||||
&buf, // output struct
|
||||
&output_size)) { // output struct size
|
||||
errx(1, "generateNonce call failed");
|
||||
}
|
||||
|
||||
// report the nonce SHA1 hash
|
||||
for (idx = 0; idx < output_size; idx++) {
|
||||
printf("%s%02X", (idx ? " " : ""), buf[idx]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2010 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 _EFFACEABLE_H
|
||||
#define _EFFACEABLE_H
|
||||
|
||||
#include <IOKit/IOTypes.h>
|
||||
|
||||
bool connectClient(const char * name, io_service_t * service, io_connect_t * connection);
|
||||
|
||||
bool wipeStorage(io_connect_t connection);
|
||||
bool getCapacity(io_connect_t connection, IOByteCount * capacity);
|
||||
bool getBytes(io_connect_t connection, void * buf, IOByteCount offset, IOByteCount count);
|
||||
bool setBytes(io_connect_t connection, const void * buf, IOByteCount offset, IOByteCount count);
|
||||
bool isFormatted(io_connect_t connection, bool * is_formatted);
|
||||
bool formatStorage(io_connect_t connection);
|
||||
|
||||
int main(int argc, char * argv[]);
|
||||
void printUsage(int argc, char *argv[]);
|
||||
|
||||
void hexdump(const void * buf, IOByteCount count);
|
||||
void quickTest(io_connect_t connection);
|
||||
void lockerList(io_connect_t connection);
|
||||
void lockerSet(io_connect_t connection, char *tag, char *file);
|
||||
void lockerGet(io_connect_t connection, char *tag, char *file);
|
||||
void lockerEfface(io_connect_t connection, char *tag);
|
||||
void generateNonce(io_connect_t connection);
|
||||
|
||||
|
||||
#endif /* _EFFACEABLE_H */
|
||||
Vendored
+560
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Copyright (C) 2007-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.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <list.h>
|
||||
#include <lib/env.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/menu.h>
|
||||
|
||||
static struct list_node env_list = LIST_INITIAL_VALUE(env_list);
|
||||
|
||||
#define ENV_VAR_ENTRY_NAME_LEN 64
|
||||
|
||||
struct env_var_entry {
|
||||
struct list_node node;
|
||||
struct env_var var;
|
||||
char name[ENV_VAR_ENTRY_NAME_LEN];
|
||||
};
|
||||
|
||||
static int str2ip(const char *str, uint32_t *ip);
|
||||
static int str2mac(const char *str, uint8_t addr[6]);
|
||||
|
||||
#define kIBootHiddenEnvDomain "com.apple.System."
|
||||
|
||||
#if DEBUG_BUILD
|
||||
static bool nvram_whitelist_override = false;
|
||||
#else
|
||||
static const bool nvram_whitelist_override = false;
|
||||
#endif
|
||||
|
||||
static bool hide_key(const char *str)
|
||||
{
|
||||
if (0 == strncmp(str, kIBootHiddenEnvDomain, sizeof(kIBootHiddenEnvDomain)-1))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct env_var_entry *find_var_entry(const char *name)
|
||||
{
|
||||
struct env_var_entry *entry;
|
||||
|
||||
list_for_every_entry(&env_list, entry, struct env_var_entry, node) {
|
||||
if (!strcmp(entry->name, name))
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct env_var *env_get_etc(const char *name)
|
||||
{
|
||||
struct env_var_entry *entry;
|
||||
|
||||
entry = find_var_entry(name);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
if ((entry->var.flags & ENV_SHADOW) && !nvram_whitelist_override) {
|
||||
dprintf(DEBUG_CRITICAL, "\x1b[1;5;31mBlocked read\x1b[m from shadowed variable \"%s\"\n", name);
|
||||
dprintf(DEBUG_CRITICAL, "Disable NVRAM whitelist with DEBUG iBoot command envprot off\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &entry->var;
|
||||
}
|
||||
|
||||
const char *env_get(const char *name)
|
||||
{
|
||||
const struct env_var *var;
|
||||
|
||||
var = env_get_etc(name);
|
||||
if (!var)
|
||||
return NULL;
|
||||
|
||||
return var->str;
|
||||
}
|
||||
|
||||
size_t env_get_uint(const char *name, size_t default_val)
|
||||
{
|
||||
const struct env_var *var;
|
||||
|
||||
var = env_get_etc(name);
|
||||
if (!var)
|
||||
return default_val;
|
||||
|
||||
return var->u;
|
||||
}
|
||||
|
||||
bool env_get_bool(const char *name, bool default_val)
|
||||
{
|
||||
const struct env_var *var;
|
||||
|
||||
var = env_get_etc(name);
|
||||
if (NULL == var)
|
||||
return default_val;
|
||||
|
||||
if (!strcmp(var->str, "true"))
|
||||
return true;
|
||||
if (0 != var->u)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int str2ip(const char *str, uint32_t *ip)
|
||||
{
|
||||
char numstr[16];
|
||||
int num;
|
||||
int field = 0;
|
||||
const char *ptr;
|
||||
|
||||
if (str == NULL)
|
||||
return -1;
|
||||
|
||||
*ip = 0;
|
||||
|
||||
ptr = str;
|
||||
for (field = 0; field < 4; field++) {
|
||||
char *dot = strchr(ptr, '.');
|
||||
|
||||
if (dot) {
|
||||
strlcpy(numstr, ptr, __min((unsigned)(dot - ptr + 1), sizeof(numstr)));
|
||||
} else {
|
||||
strlcpy(numstr, ptr, sizeof(numstr));
|
||||
}
|
||||
|
||||
// printf("numstr '%s'\n", numstr);
|
||||
|
||||
num = strtol(numstr, NULL, 0);
|
||||
if (num < 0 || num > 255) {
|
||||
return -1;
|
||||
}
|
||||
*ip |= (num << (8 * field));
|
||||
|
||||
if (dot)
|
||||
ptr = dot + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int env_get_ipaddr(const char *name, uint32_t *ip)
|
||||
{
|
||||
const char *env;
|
||||
|
||||
env = env_get(name);
|
||||
if (!env)
|
||||
return -1;
|
||||
|
||||
if (str2ip(env, ip) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int str2mac(const char *str, uint8_t addr[6])
|
||||
{
|
||||
char numstr[16];
|
||||
int num;
|
||||
int field = 0;
|
||||
const char *ptr;
|
||||
char *endptr;
|
||||
|
||||
if (str == NULL)
|
||||
return -1;
|
||||
|
||||
addr[0] = addr[1] = addr[2] = addr[3] = addr[4] = addr[5] = 0;
|
||||
|
||||
ptr = str;
|
||||
for (field = 0; field < 6; field++) {
|
||||
char *sep = strchr(ptr, ':');
|
||||
|
||||
if (sep) {
|
||||
strlcpy(numstr, ptr, __min((unsigned)(sep - ptr + 1), sizeof(numstr)));
|
||||
} else {
|
||||
strlcpy(numstr, ptr, sizeof(numstr));
|
||||
}
|
||||
|
||||
// printf("numstr '%s'\n", numstr);
|
||||
|
||||
num = strtol(numstr, &endptr, 16);
|
||||
if (endptr == numstr) {
|
||||
dprintf(DEBUG_INFO, "malformed MAC address %s\n", str);
|
||||
return -1;
|
||||
}
|
||||
if (num < 0 || num > 255) {
|
||||
dprintf(DEBUG_INFO, "makformed MAC address %s\n", str);
|
||||
return -1;
|
||||
}
|
||||
addr[field] = num;
|
||||
|
||||
if (sep)
|
||||
ptr = sep + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int env_get_ethaddr(const char *name, uint8_t ethaddr[6])
|
||||
{
|
||||
const char *env;
|
||||
|
||||
env = env_get(name);
|
||||
if (!env)
|
||||
return -1;
|
||||
|
||||
if (str2mac(env, ethaddr) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int env_set(const char *name, const char *val, uint32_t flags)
|
||||
{
|
||||
struct env_var_entry *entry;
|
||||
|
||||
if (flags & ENV_PERSISTENT) {
|
||||
if (env_blacklist_nvram(name))
|
||||
flags |= ENV_SHADOW;
|
||||
}
|
||||
|
||||
/* see if it already exists */
|
||||
entry = find_var_entry(name);
|
||||
if (entry) {
|
||||
if ((flags & ENV_SHADOW) && !(entry->var.flags & ENV_SHADOW) && !nvram_whitelist_override) {
|
||||
dprintf(DEBUG_CRITICAL, "\x1b[1;5;31mBlocked shadowed write\x1b[m to variable \"%s\"\n", name);
|
||||
dprintf(DEBUG_CRITICAL, "Disable NVRAM whitelist with DEBUG iBoot command envprot off\n");
|
||||
// If the variable exists, is not on the whitelist for NVRAM variables,
|
||||
// and the value was generated internally (probably from the default environment),
|
||||
// refuse to modify the existing value.
|
||||
// This will result in the variable getting removed from nvram on saveenv, but
|
||||
// protects us from people removing things from the default environment.
|
||||
return -1;
|
||||
} else {
|
||||
// If the variable exists and either is on the whitelist or was not
|
||||
// generated internally, delete the old value
|
||||
env_unset(name);
|
||||
}
|
||||
}
|
||||
|
||||
entry = malloc(sizeof(struct env_var_entry));
|
||||
|
||||
strlcpy(entry->name, name, sizeof(entry->name));
|
||||
|
||||
entry->var.str = strdup(val);
|
||||
if (entry->var.str == NULL) {
|
||||
free(entry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry->var.u = strtoul(entry->var.str, NULL, 0);
|
||||
|
||||
/* save the flags */
|
||||
entry->var.flags = flags;
|
||||
|
||||
list_add_tail(&env_list, &entry->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int env_set_uint(const char *name, size_t val, uint32_t flags)
|
||||
{
|
||||
char numbuf[(sizeof(size_t) * 2) + 3]; // plus '3' to store "0x" and null terminator
|
||||
|
||||
snprintf(numbuf, sizeof(numbuf), "0x%lx", val);
|
||||
|
||||
return env_set(name, numbuf, flags);
|
||||
}
|
||||
|
||||
int env_unset(const char *name)
|
||||
{
|
||||
struct env_var_entry *entry;
|
||||
|
||||
entry = find_var_entry(name);
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
list_delete(&entry->node);
|
||||
|
||||
free(entry->var.str);
|
||||
free(entry);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* used by the nvram code to write the environment out to nvram.
|
||||
* serialize all of the persistent environment variables with a simple
|
||||
* <name>=<value>NULL...
|
||||
* strategy.
|
||||
*/
|
||||
size_t env_serialize(uint8_t *buf, size_t buf_len)
|
||||
{
|
||||
size_t offset;
|
||||
struct env_var_entry *entry;
|
||||
|
||||
offset = 0;
|
||||
list_for_every_entry(&env_list, entry, struct env_var_entry, node) {
|
||||
if (entry->var.flags & ENV_PERSISTENT) {
|
||||
/* print one var into the buffer, avoiding overflow */
|
||||
offset += snprintf((void *)(buf + offset), buf_len - offset, "%s=%s", entry->name, entry->var.str) + 1;
|
||||
|
||||
/* effectively require there to always be at least one free byte */
|
||||
if (offset >= buf_len)
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* opposite of the above */
|
||||
int env_unserialize(const uint8_t *buf, size_t buf_len)
|
||||
{
|
||||
size_t offset;
|
||||
|
||||
#if 0
|
||||
dprintf(DEBUG_SPEW, "serialized environment vars:\n");
|
||||
dhexdump(DEBUG_SPEW, buf, buf_len);
|
||||
#endif
|
||||
|
||||
offset = 0;
|
||||
while (offset < buf_len) {
|
||||
char envname[64];
|
||||
char envval[256];
|
||||
uint32_t var_start, var_end;
|
||||
uint32_t val_start, val_end;
|
||||
|
||||
if (buf[offset] == 0) {
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* find the start and end of the variable name */
|
||||
var_start = offset;
|
||||
for (var_end = var_start; buf[var_end] != '=' && buf[var_end] != 0; var_end++)
|
||||
;
|
||||
if (buf[var_end] == 0) // malformed variable, no '='
|
||||
break;
|
||||
|
||||
/* find the start and end of the value component */
|
||||
val_start = var_end + 1; /* skip the '=' */
|
||||
for (val_end = val_start; buf[val_end] != 0; val_end++)
|
||||
;
|
||||
|
||||
/* copy them out */
|
||||
strlcpy(envname, (const char *)&buf[var_start], __min(var_end - var_start + 1, sizeof(envname)));
|
||||
strlcpy(envval, (const char *)&buf[val_start], __min(val_end - val_start + 1, sizeof(envval)));
|
||||
|
||||
dprintf(DEBUG_SPEW, "'%s' = '%s'\n", envname, envval);
|
||||
|
||||
/* load the environment */
|
||||
if (strlen(envname) > 0 && strlen(envval) > 0) {
|
||||
uint32_t flags = ENV_PERSISTENT;
|
||||
if (env_blacklist_nvram(envname))
|
||||
flags |= ENV_SHADOW;
|
||||
env_set(envname, envval, flags);
|
||||
}
|
||||
|
||||
offset = val_end + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for unit testing testing purposes only, remove every environment
|
||||
* variable
|
||||
*/
|
||||
void env_reset(void)
|
||||
{
|
||||
struct env_var_entry *entry;
|
||||
struct env_var_entry *temp;
|
||||
|
||||
list_for_every_entry_safe(&env_list, entry, temp, struct env_var_entry, node) {
|
||||
env_unset(entry->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* command prompt routines */
|
||||
static void dump_var(const struct env_var_entry *entry)
|
||||
{
|
||||
int i, binary = 0;
|
||||
uint32_t flags;
|
||||
|
||||
/* filter out non-ascii strings in variable values */
|
||||
for (i = 0; i < ENV_VAR_ENTRY_NAME_LEN; i++) {
|
||||
if (entry->var.str[i] == 0)
|
||||
break;
|
||||
if (entry->var.str[i] < 0x20 || entry->var.str[i] > 0x7e)
|
||||
binary = 1;
|
||||
}
|
||||
|
||||
flags = entry->var.flags;
|
||||
printf("%c %s = %s%s%s\n",
|
||||
(flags & ENV_SHADOW) ? 'S' : ((flags & ENV_PERSISTENT) ? 'P' : ' '),
|
||||
entry->name,
|
||||
binary ? "" : "\"",
|
||||
binary ? "<DATA>" : entry->var.str,
|
||||
binary ? "" : "\"");
|
||||
}
|
||||
|
||||
int do_printenv(int argc, struct cmd_arg *args)
|
||||
{
|
||||
struct env_var_entry *entry;
|
||||
|
||||
if (argc >= 2) {
|
||||
/* display a single variable */
|
||||
entry = find_var_entry(args[1].str);
|
||||
if (entry) {
|
||||
if (!hide_key(entry->name))
|
||||
dump_var(entry);
|
||||
} else {
|
||||
printf("variable %s not set\n", args[1].str);
|
||||
}
|
||||
} else {
|
||||
/* display all variables */
|
||||
list_for_every_entry(&env_list, entry, struct env_var_entry, node) {
|
||||
if (hide_key(entry->name)) continue;
|
||||
dump_var(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_getenv(int argc, struct cmd_arg *args)
|
||||
{
|
||||
struct env_var_entry *entry;
|
||||
if(argc != 2)
|
||||
{
|
||||
#if !RELEASE_BUILD
|
||||
printf("%s <var>\n", args[0].str);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry = find_var_entry(args[1].str);
|
||||
if(!entry)
|
||||
return -1;
|
||||
if(hide_key(entry->name))
|
||||
return -1;
|
||||
if(env_blacklist(entry->name, false))
|
||||
return -1;
|
||||
return env_set("cmd-results", entry->var.str, 0);
|
||||
}
|
||||
|
||||
int do_setenv(int argc, struct cmd_arg *args)
|
||||
{
|
||||
if (argc < 2) {
|
||||
#if !RELEASE_BUILD
|
||||
printf("not enough arguments.\n");
|
||||
printf("%s <var> [<value>] ...\n", args[0].str);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hide_key(args[1].str)) return -1;
|
||||
|
||||
if (env_blacklist(args[1].str, true)) return -1;
|
||||
|
||||
if (argc < 3) {
|
||||
env_unset(args[1].str);
|
||||
} else {
|
||||
size_t buf_size = 512;
|
||||
char *envbuf = malloc(buf_size);
|
||||
int i;
|
||||
|
||||
/* reassemble a string from all the command line args */
|
||||
envbuf[0] = 0;
|
||||
for (i = 2; i < argc; i++) {
|
||||
strlcat(envbuf, args[i].str, buf_size);
|
||||
if (i != (argc - 1))
|
||||
strlcat(envbuf, " ", buf_size);
|
||||
}
|
||||
|
||||
env_set(args[1].str, envbuf, ENV_PERSISTENT);
|
||||
|
||||
free(envbuf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_clearenv(int argc, struct cmd_arg *args)
|
||||
{
|
||||
struct env_var_entry *entry, *pentry;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("not enough arguments.\n");
|
||||
printf("Usage: It's a two step process. Run the following commands to clear\n");
|
||||
printf("\t\tclearenv <var>\n");
|
||||
printf("\t\tclearenv 1 \t {confirms the clear}\n");
|
||||
printf("ex: clearenv auto-boot boot-args\n");
|
||||
printf(" clearenv 1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (args[1].u == 0) {
|
||||
printf("Clear pending. Run command \"clearenv 1\" to confirm\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* clear all persistent variables */
|
||||
while (1) {
|
||||
pentry = 0;
|
||||
list_for_every_entry(&env_list, entry, struct env_var_entry, node) {
|
||||
if (hide_key(entry->name)) continue;
|
||||
if (entry->var.flags & ENV_PERSISTENT) {
|
||||
pentry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pentry == 0) break;
|
||||
|
||||
env_unset(pentry->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if DEBUG_BUILD
|
||||
int
|
||||
do_envprot(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (argc != 2) {
|
||||
result = -1;
|
||||
} else {
|
||||
if (strcmp(args[1].str, "off") == 0) {
|
||||
nvram_whitelist_override = true;
|
||||
printf("Disabled NVRAM whitelist\n");
|
||||
} else if (strcmp(args[1].str, "on") == 0) {
|
||||
nvram_whitelist_override = false;
|
||||
printf("Enabled NVRAM whitelist\n");
|
||||
} else {
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (result < 0)
|
||||
printf("Usage: envprot {on | off}\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LIBENV_DIR := $(GET_LOCAL_DIR)
|
||||
LIBENV_BUILD := $(call TOLIBDIR,$(LIBENV_DIR)/LIBENV.a)
|
||||
COMMONLIBS += LIBENV
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
LIBENV_OBJS := $(LIBENV_DIR)/env.o
|
||||
|
||||
LIBENV_OBJS := $(call TOLIBOBJDIR,$(LIBENV_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBENV_OBJS:%o=%d)
|
||||
|
||||
$(LIBENV_BUILD): $(LIBENV_OBJS)
|
||||
|
||||
endif
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
OPTIONS += WITH_ENV=1
|
||||
LIBRARY_MODULES += lib/env
|
||||
Vendored
+231
@@ -0,0 +1,231 @@
|
||||
#include <unittest.h>
|
||||
#include <lib/env.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern int do_printenv(int argc, void *);
|
||||
extern void env_reset(void);
|
||||
|
||||
/* Before each test case is called, empty out the environment */
|
||||
void test_env_suite_setup(void)
|
||||
{
|
||||
env_reset();
|
||||
}
|
||||
|
||||
void test_env(uintptr_t context)
|
||||
{
|
||||
const uint8_t source[] =
|
||||
"numvar=1234567890\0"
|
||||
"truevar=true\0"
|
||||
"falsevar=false\0"
|
||||
"zerovar=0\0"
|
||||
"strvar=hi mom 123=abc-_*\0"
|
||||
"ethaddr=12:34:56:78:ab:cd\0"
|
||||
"ipaddr=32.1.0.100\0"
|
||||
"blacklisted=nocando\0"
|
||||
"com.apple.System.hideme=hidden";
|
||||
uint8_t ethaddr[6];
|
||||
uint32_t ipaddr;
|
||||
|
||||
TEST_ASSERT_EQ(env_unserialize(source, sizeof(source)), 0);
|
||||
|
||||
// Test getting variables as strings
|
||||
TEST_ASSERT_STR_EQ(env_get("numvar"), "1234567890");
|
||||
TEST_ASSERT_STR_EQ(env_get("truevar"), "true");
|
||||
TEST_ASSERT_STR_EQ(env_get("falsevar"), "false");
|
||||
TEST_ASSERT_STR_EQ(env_get("zerovar"), "0");
|
||||
TEST_ASSERT_STR_EQ(env_get("strvar"), "hi mom 123=abc-_*");
|
||||
TEST_ASSERT_STR_EQ(env_get("ethaddr"), "12:34:56:78:ab:cd");
|
||||
TEST_ASSERT_STR_EQ(env_get("ipaddr"), "32.1.0.100");
|
||||
TEST_ASSERT_STR_EQ(env_get("com.apple.System.hideme"), "hidden");
|
||||
|
||||
// Test getting variables as booleans
|
||||
TEST_ASSERT_EQ(env_get_bool("truevar", false), true);
|
||||
TEST_ASSERT_EQ(env_get_bool("truevar", true), true);
|
||||
TEST_ASSERT_EQ(env_get_bool("falsevar", false), false);
|
||||
TEST_ASSERT_EQ(env_get_bool("falsevar", true), false);
|
||||
TEST_ASSERT_EQ(env_get_bool("zerovar", false), false);
|
||||
TEST_ASSERT_EQ(env_get_bool("zerovar", true), false);
|
||||
TEST_ASSERT_EQ(env_get_bool("numvar", false), true);
|
||||
TEST_ASSERT_EQ(env_get_bool("numvar", true), true);
|
||||
TEST_ASSERT_EQ(env_get_bool("strvar", false), false);
|
||||
TEST_ASSERT_EQ(env_get_bool("strvar", true), false);
|
||||
// Test default value for env_get_bool
|
||||
TEST_ASSERT_EQ(env_get_bool("notavar", true), true);
|
||||
TEST_ASSERT_EQ(env_get_bool("notavar", false), false);
|
||||
|
||||
// Test getting variables as numbers
|
||||
TEST_ASSERT_EQ(env_get_uint("numvar", 0), 1234567890ULL);
|
||||
// Test default value for env_get_uint
|
||||
TEST_ASSERT_EQ(env_get_uint("notavar", 123456), 123456);
|
||||
|
||||
// Test getting variables as MAC addresses
|
||||
TEST_ASSERT_EQ(env_get_ethaddr("ethaddr", ethaddr), 0);
|
||||
TEST_ASSERT_MEM_EQ(ethaddr, TEST_ARRAY(uint8_t, 0x12, 0x34, 0x56, 0x78, 0xab, 0xcd), sizeof(ethaddr));
|
||||
// Test rejecting malformed MAC addresses
|
||||
TEST_ASSERT_NEQ(env_get_ethaddr("strvar", ethaddr), 0);
|
||||
TEST_ASSERT_NEQ(env_get_ethaddr("numvar", ethaddr), 0);
|
||||
TEST_ASSERT_NEQ(env_get_ethaddr("notavar", ethaddr), 0);
|
||||
|
||||
// Test getting variables as IP addresses
|
||||
TEST_ASSERT_EQ(env_get_ipaddr("ipaddr", &ipaddr), 0);
|
||||
TEST_ASSERT_EQ(ipaddr, 0x64000120);
|
||||
|
||||
// Test getting a blacklisted variable fails
|
||||
TEST_ASSERT_NULL(env_get("blacklisted"));
|
||||
|
||||
// Test setting a non-persistent variable
|
||||
TEST_ASSERT_EQ(env_set("temp", "don't save me", 0), 0);
|
||||
TEST_ASSERT_STR_EQ(env_get("temp"), "don't save me");
|
||||
|
||||
// Test setting a persistent variable
|
||||
TEST_ASSERT_EQ(env_set("persist", "save this var", ENV_PERSISTENT), 0);
|
||||
TEST_ASSERT_STR_EQ(env_get("persist"), "save this var");
|
||||
|
||||
// Test env_set_uint
|
||||
TEST_ASSERT_EQ(env_set_uint("uint", 0xabcd1234, 0), 0);
|
||||
TEST_ASSERT_EQ(env_get_uint("uint", 0), 0xabcd1234);
|
||||
|
||||
// Test overwriting a persistent variable
|
||||
TEST_ASSERT_EQ(env_set("overwrite", "will not be saved", ENV_PERSISTENT), 0);
|
||||
TEST_ASSERT_STR_EQ(env_get("overwrite"), "will not be saved");
|
||||
TEST_ASSERT_EQ(env_set("overwrite", "save this value", ENV_PERSISTENT), 0);
|
||||
TEST_ASSERT_STR_EQ(env_get("overwrite"), "save this value");
|
||||
|
||||
// Test unsetting a value
|
||||
TEST_ASSERT_EQ(env_set("unset", "about to unset", ENV_PERSISTENT), 0);
|
||||
TEST_ASSERT_STR_EQ(env_get("unset"), "about to unset");
|
||||
TEST_ASSERT_EQ(env_unset("unset"), 1);
|
||||
TEST_ASSERT_NULL(env_get("unset"));
|
||||
// unsetting a non-existant variable should fail
|
||||
TEST_ASSERT_EQ(env_unset("unset"), 0);
|
||||
|
||||
// Test serializing - everything from the source should be serialized,
|
||||
// plus persistent variables set with env_set
|
||||
const uint8_t expected[] =
|
||||
"numvar=1234567890\0"
|
||||
"truevar=true\0"
|
||||
"falsevar=false\0"
|
||||
"zerovar=0\0"
|
||||
"strvar=hi mom 123=abc-_*\0"
|
||||
"ethaddr=12:34:56:78:ab:cd\0"
|
||||
"ipaddr=32.1.0.100\0"
|
||||
"blacklisted=nocando\0"
|
||||
"com.apple.System.hideme=hidden\0"
|
||||
"persist=save this var\0"
|
||||
"overwrite=save this value";
|
||||
|
||||
uint8_t *serialized = malloc(sizeof(expected) * 2);
|
||||
|
||||
TEST_ASSERT_EQ(env_serialize(serialized, sizeof(expected) * 2), sizeof(expected));
|
||||
TEST_ASSERT_MEM_EQ(serialized, expected, sizeof(expected));
|
||||
|
||||
memset(serialized, 0, sizeof(expected));
|
||||
TEST_ASSERT_EQ(env_serialize(serialized, sizeof(expected) + 1), sizeof(expected));
|
||||
TEST_ASSERT_MEM_EQ(serialized, expected, sizeof(expected));
|
||||
|
||||
// Too small buffer should fail serialization
|
||||
TEST_ASSERT_EQ(env_serialize(serialized, sizeof(expected) - 1), 0);
|
||||
}
|
||||
|
||||
void test_env_max_len_var(uintptr_t context)
|
||||
{
|
||||
int result;
|
||||
size_t serialized_len;
|
||||
const char *long_var =
|
||||
"0001020304050607"
|
||||
"08090a0b0c0d0e0f"
|
||||
"1011121314151617"
|
||||
"18191a1b1c1d1e1f"
|
||||
"2021222324252627"
|
||||
"28292a2b2c2d2e2f"
|
||||
"3031323334353637"
|
||||
"38393a3b3c3d3e3f"
|
||||
"4041424344454647"
|
||||
"48494a4b4c4d4e4f"
|
||||
"5051525354555657"
|
||||
"58595a5b5c5d5e5f"
|
||||
"6061626364656667"
|
||||
"68696a6b6c6d6e6f"
|
||||
"7071727374757677"
|
||||
"78797a7b7c7d7e_";
|
||||
|
||||
const uint8_t expected[] =
|
||||
"testvar="
|
||||
"0001020304050607"
|
||||
"08090a0b0c0d0e0f"
|
||||
"1011121314151617"
|
||||
"18191a1b1c1d1e1f"
|
||||
"2021222324252627"
|
||||
"28292a2b2c2d2e2f"
|
||||
"3031323334353637"
|
||||
"38393a3b3c3d3e3f"
|
||||
"4041424344454647"
|
||||
"48494a4b4c4d4e4f"
|
||||
"5051525354555657"
|
||||
"58595a5b5c5d5e5f"
|
||||
"6061626364656667"
|
||||
"68696a6b6c6d6e6f"
|
||||
"7071727374757677"
|
||||
"78797a7b7c7d7e_"
|
||||
"\0testvar2=abc";
|
||||
|
||||
// Setting the variable should succeed
|
||||
result = env_set("testvar", long_var, ENV_PERSISTENT);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
|
||||
// Add a dummy variable after testvar for serialization test
|
||||
result = env_set("testvar2", "abc", ENV_PERSISTENT);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
|
||||
// The variable should read back correctly
|
||||
TEST_ASSERT_STR_EQ(env_get("testvar"), long_var);
|
||||
|
||||
// The variable should serialize correctly
|
||||
uint8_t *serialized = malloc(sizeof(expected) * 2);
|
||||
serialized_len = env_serialize(serialized, sizeof(expected) * 2);
|
||||
TEST_ASSERT_EQ(serialized_len, sizeof(expected));
|
||||
TEST_ASSERT_MEM_EQ(serialized, expected, sizeof(expected));
|
||||
|
||||
// Clear out the environment to prepare for deserialization
|
||||
env_reset();
|
||||
|
||||
// Make sure the environment really was cleared
|
||||
TEST_ASSERT_NULL(env_get("testvar"));
|
||||
TEST_ASSERT_NULL(env_get("testvar2"));
|
||||
|
||||
// Now load the environment back in from the serialized buffer
|
||||
result = env_unserialize(serialized, serialized_len);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
|
||||
// And make sure everything came through OK
|
||||
TEST_ASSERT_STR_EQ(env_get("testvar"), long_var);
|
||||
TEST_ASSERT_STR_EQ(env_get("testvar2"), "abc");
|
||||
}
|
||||
|
||||
bool env_blacklist(const char *name, bool write)
|
||||
{
|
||||
if (strcmp(name, "set-blacklisted") == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool env_blacklist_nvram(const char *name)
|
||||
{
|
||||
if (strcmp(name, "blacklisted") == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct test_suite env_test_suite = {
|
||||
.name = "env",
|
||||
.description = "tests the environment module",
|
||||
.setup_function = test_env_suite_setup,
|
||||
.test_cases = {
|
||||
{ "env", test_env, 0 },
|
||||
{ "env_max_len_var", test_env_max_len_var, 0 },
|
||||
TEST_CASE_LAST
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE(env_test_suite);
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := env
|
||||
|
||||
TEST_OBJS := \
|
||||
$(LOCAL_DIR)/env.o
|
||||
|
||||
TEST_SUPPORT_OBJS := \
|
||||
tests/unittest-main.o \
|
||||
$(LOCAL_DIR)/tests.o
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009, 2014 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <lib/fs.h>
|
||||
#include <lib/env.h>
|
||||
#include <sys/menu.h>
|
||||
|
||||
int
|
||||
fs_cat_file(const char *path)
|
||||
{
|
||||
int filefd;
|
||||
char buf[512];
|
||||
struct stat stat;
|
||||
off_t resid, readsize;
|
||||
int i;
|
||||
|
||||
/* open the file */
|
||||
filefd = fs_open(path, 0);
|
||||
if (filefd < 0)
|
||||
return -1;
|
||||
|
||||
/* stat it */
|
||||
fs_stat(filefd, &stat);
|
||||
|
||||
resid = stat.st_size;
|
||||
while (resid) {
|
||||
readsize = sizeof(buf);
|
||||
if (readsize > resid)
|
||||
readsize = resid;
|
||||
fs_read(filefd, (void *)buf, readsize);
|
||||
for (i = 0; i < readsize; i++)
|
||||
printf("%c", buf[i]);
|
||||
resid -= readsize;
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
fs_close(filefd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
void
|
||||
fs_stat_dump(const struct stat *st)
|
||||
{
|
||||
printf("STAT (%p):\n", (void *) st);
|
||||
printf(" st_ino: %x\n", st->st_ino);
|
||||
printf(" st_size %x\n", st->st_size);
|
||||
printf(" st_blocks: %x\n", st->st_blocks);
|
||||
printf(" st_flags: %x\n", st->st_flags);
|
||||
printf(" st_type: ");
|
||||
switch (st->st_type) {
|
||||
case FILE_TYPE_FILE:
|
||||
printf("FILE\n");
|
||||
break;
|
||||
case FILE_TYPE_DIR:
|
||||
printf("DIR\n");
|
||||
break;
|
||||
default:
|
||||
printf("UNKNOWN\n");
|
||||
}
|
||||
|
||||
printf(" st_ctime: %x\n", st->st_ctime);
|
||||
printf(" st_mtime: %x\n", st->st_mtime);
|
||||
printf(" st_atime: %x\n", st->st_atime);
|
||||
}
|
||||
|
||||
void
|
||||
fs_statfs_dump(const struct statfs *st)
|
||||
{
|
||||
printf("STATFS (%p):\n", (void *) st);
|
||||
printf(" f_bsize: %x\n", st->f_bsize);
|
||||
printf(" f_blocks: %x\n", st->f_blocks);
|
||||
printf(" f_bfree: %x\n", st->f_bfree);
|
||||
}
|
||||
|
||||
int fs_dump_dir(const char *dirpath, bool recurse)
|
||||
{
|
||||
int dirfd;
|
||||
struct dirent ent;
|
||||
|
||||
printf("fs_dump_dir: dumping contents of directory '%s'\n", dirpath);
|
||||
|
||||
dirfd = fs_opendir(dirpath);
|
||||
if (dirfd < 0) {
|
||||
printf("error opening dir '%s'\n", dirpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fs_readdir(dirfd, &ent) >= 0) {
|
||||
printf("\tname '%s', size %d, type 0x%x\n", ent.d_name, ent.d_reclen, ent.d_type);
|
||||
}
|
||||
fs_closedir(dirfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_fs(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int err;
|
||||
size_t max_file_size;
|
||||
|
||||
if (!security_allow_modes(kSecurityModeDebugCmd)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
notenoughargs:
|
||||
printf("not enough arguments.\n");
|
||||
usage:
|
||||
printf("usage:\n");
|
||||
printf("%s mount <fs> <device> <dir>\n", args[0].str);
|
||||
printf("%s unmount <dir>\n", args[0].str);
|
||||
printf("%s mounts\n", args[0].str);
|
||||
printf("%s dir <path>\n", args[0].str);
|
||||
printf("%s load <path> <loadaddress>\n", args[0].str);
|
||||
printf("%s cat <path>\n", args[0].str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(args[1].str, "mount")) {
|
||||
if (argc < 5)
|
||||
goto notenoughargs;
|
||||
|
||||
err = fs_mount(args[3].str, args[2].str, args[4].str);
|
||||
printf("mount returned %d\n", err);
|
||||
} else if (!strcmp(args[1].str, "unmount")) {
|
||||
if (argc < 3)
|
||||
goto notenoughargs;
|
||||
|
||||
fs_unmount(args[2].str);
|
||||
} else if (!strcmp(args[1].str, "mounts")) {
|
||||
fs_dump_mounts();
|
||||
} else if (!strcmp(args[1].str, "dir")) {
|
||||
if (argc < 3)
|
||||
goto notenoughargs;
|
||||
|
||||
fs_dump_dir(args[2].str, false);
|
||||
} else if (!strcmp(args[1].str, "load")) {
|
||||
if (argc < 4)
|
||||
goto notenoughargs;
|
||||
|
||||
/* Allow file to be up to 32 MB. */
|
||||
max_file_size = 0x02000000;
|
||||
if (!security_allow_memory((void *)args[3].u, max_file_size)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = fs_load_file(args[2].str, args[3].u, &max_file_size);
|
||||
printf("fs_load_file() returns %d\n", err);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
env_set_uint("loadaddr", args[3].u, 0);
|
||||
env_set_uint("filesize", max_file_size, 0);
|
||||
} else if (!strcmp(args[1].str, "cat")) {
|
||||
if (argc < 3)
|
||||
goto notenoughargs;
|
||||
|
||||
err = fs_cat_file(args[2].str);
|
||||
} else {
|
||||
printf("unrecognized subcommand.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/security.h>
|
||||
#include <lib/fs.h>
|
||||
#include <list.h>
|
||||
#include <platform.h>
|
||||
|
||||
#ifndef MAX_FDS
|
||||
/** Maximum number of open files/directories */
|
||||
# define MAX_FDS 16
|
||||
#endif
|
||||
|
||||
//#if WITH_HFS
|
||||
extern const struct fs_ops hfs_fs_ops;
|
||||
//#endif
|
||||
|
||||
static const struct fs_struct fs_list[] = {
|
||||
//#if WITH_HFS
|
||||
{ "hfs", &hfs_fs_ops },
|
||||
//#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
struct fscookie {
|
||||
const struct fs_struct *fs;
|
||||
void *ctxt;
|
||||
};
|
||||
|
||||
#define FILEMAGIC 'film'
|
||||
#define DIRMAGIC 'dirm'
|
||||
|
||||
struct filecookie {
|
||||
struct list_node node;
|
||||
struct fscookie *fscookie;
|
||||
int fd;
|
||||
void *ctxt;
|
||||
bool is_dir;
|
||||
};
|
||||
|
||||
struct fsmount {
|
||||
struct list_node node;
|
||||
struct fscookie *cookie;
|
||||
size_t mountpoint_len;
|
||||
char mountpoint[256];
|
||||
};
|
||||
|
||||
/** list of active mountpoints */
|
||||
static struct list_node mounts = LIST_INITIAL_VALUE(mounts);
|
||||
|
||||
/** list of open files */
|
||||
static struct list_node files = LIST_INITIAL_VALUE(files);
|
||||
|
||||
/**
|
||||
* Given a file descriptor, obtain the corresponding filecookie.
|
||||
*
|
||||
* \param[in] handle The file descriptor to look up.
|
||||
*
|
||||
* \return Pointer to the corresponding filecookie structure.
|
||||
* \retval NULL The file descriptor is not currently allocated.
|
||||
*/
|
||||
static struct filecookie *fhtocp(int handle)
|
||||
{
|
||||
struct filecookie *file;
|
||||
|
||||
list_for_every_entry(&files, file, struct filecookie, node) {
|
||||
if (file->fd == handle)
|
||||
return file;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a valid, unused file descriptor.
|
||||
*
|
||||
* \return A file descriptor not currently in use.
|
||||
* \retval -1 No file descriptors are available.
|
||||
*/
|
||||
static int getfd(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
for (fd = 0; fd < MAX_FDS; fd++) {
|
||||
if (NULL == fhtocp(fd))
|
||||
return fd;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct fsmount *path_to_mount(const char *path, const char **outpath)
|
||||
{
|
||||
struct fsmount *mount;
|
||||
|
||||
/* take a path and see if it lies on a mountpoint */
|
||||
list_for_every_entry(&mounts, mount, struct fsmount, node) {
|
||||
if (!strncmp(path, mount->mountpoint, mount->mountpoint_len)) {
|
||||
if (path[mount->mountpoint_len] == '/') {
|
||||
/* the leading chars matched the mountpoint exactly and the next char was a / */
|
||||
*outpath = &path[mount->mountpoint_len];
|
||||
// printf("path_to_mount: path '%s, found mount %p, path '%s', outpath '%s'\n",
|
||||
// path, mount, mount->mountpoint, *outpath);
|
||||
return mount;
|
||||
}
|
||||
if (path[mount->mountpoint_len] == 0) {
|
||||
/* the leading chars matched the mountpoint and there was no more string,
|
||||
* return a / for the file system to chew on */
|
||||
*outpath = "/";
|
||||
return mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fs_mount(const char *dev, const char *fs_name, const char *mountpoint)
|
||||
{
|
||||
struct fscookie *cookie;
|
||||
struct fsmount *mount;
|
||||
const struct fs_struct *fs;
|
||||
void *ctxt;
|
||||
int err;
|
||||
|
||||
ASSERT(dev);
|
||||
ASSERT(fs_name);
|
||||
|
||||
for (fs = fs_list; fs->name != NULL; fs++) {
|
||||
if (!strcmp(fs_name, fs->name))
|
||||
break;
|
||||
}
|
||||
if (fs->name == NULL) {
|
||||
dprintf(DEBUG_CRITICAL, "fs_mount: fs_name '%s' not in registered fs list\n", fs_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
list_for_every_entry(&mounts, mount, struct fsmount, node) {
|
||||
if (!strcmp(mountpoint, mount->mountpoint)) {
|
||||
dprintf(DEBUG_CRITICAL, "fs_mount: mountpoint '%s' already occupied\n", mountpoint);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
err = fs->ops->mount(&ctxt, dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
cookie = malloc(sizeof(struct fscookie));
|
||||
|
||||
cookie->fs = fs;
|
||||
cookie->ctxt = ctxt;
|
||||
|
||||
/* create a mount structure */
|
||||
mount = malloc(sizeof(struct fsmount));
|
||||
|
||||
mount->cookie = cookie;
|
||||
strlcpy(mount->mountpoint, mountpoint, sizeof(mount->mountpoint));
|
||||
mount->mountpoint_len = strlen(mount->mountpoint); // used in path comparisons
|
||||
|
||||
list_add_tail(&mounts, &mount->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fs_unmount(const char *dev)
|
||||
{
|
||||
struct fsmount *mount;
|
||||
const char *shortpath;
|
||||
|
||||
ASSERT(dev);
|
||||
|
||||
mount = path_to_mount(dev, &shortpath);
|
||||
if (!mount)
|
||||
return;
|
||||
|
||||
ASSERT(mount->cookie != NULL);
|
||||
|
||||
mount->cookie->fs->ops->unmount(mount->cookie->ctxt);
|
||||
|
||||
/* remove the mount structure */
|
||||
list_delete(&mount->node);
|
||||
free(mount->cookie);
|
||||
free(mount);
|
||||
}
|
||||
|
||||
int fs_fsstat(void *fscookie, struct statfs *stat)
|
||||
{
|
||||
struct fscookie *cookie = (struct fscookie *)fscookie;
|
||||
|
||||
ASSERT(cookie);
|
||||
ASSERT(stat);
|
||||
|
||||
return cookie->fs->ops->fsstat(cookie->ctxt, stat);
|
||||
}
|
||||
|
||||
int fs_open(const char *path, uint8_t flags)
|
||||
{
|
||||
struct fsmount *mount;
|
||||
void *ctxt;
|
||||
struct filecookie *fcookie;
|
||||
const char *shortpath;
|
||||
int fd;
|
||||
|
||||
ASSERT(path);
|
||||
|
||||
mount = path_to_mount(path, &shortpath);
|
||||
if (!mount)
|
||||
return -1;
|
||||
|
||||
if (-1 == (fd = getfd()))
|
||||
return -1;
|
||||
|
||||
ctxt = mount->cookie->fs->ops->open(mount->cookie->ctxt, shortpath, flags);
|
||||
if (!ctxt)
|
||||
return -1;
|
||||
|
||||
fcookie = malloc(sizeof(struct filecookie));
|
||||
|
||||
fcookie->fscookie = mount->cookie;
|
||||
fcookie->ctxt = ctxt;
|
||||
fcookie->is_dir = false;
|
||||
fcookie->fd = fd;
|
||||
|
||||
list_add_head(&files, &fcookie->node);
|
||||
|
||||
return fcookie->fd;
|
||||
}
|
||||
|
||||
int fs_read(int handle, void *buf, size_t len)
|
||||
{
|
||||
struct filecookie *fcookie = fhtocp(handle);
|
||||
|
||||
ASSERT(fcookie);
|
||||
ASSERT(!fcookie->is_dir);
|
||||
|
||||
return fcookie->fscookie->fs->ops->read(fcookie->ctxt, buf, len);
|
||||
}
|
||||
|
||||
int fs_stat(int handle, struct stat *st)
|
||||
{
|
||||
struct filecookie *fcookie = fhtocp(handle);
|
||||
|
||||
ASSERT(fcookie);
|
||||
ASSERT(!fcookie->is_dir);
|
||||
|
||||
return fcookie->fscookie->fs->ops->fstat(fcookie->ctxt, st);
|
||||
}
|
||||
|
||||
off_t fs_seek(int handle, off_t offset, int whence)
|
||||
{
|
||||
struct filecookie *fcookie = fhtocp(handle);
|
||||
|
||||
ASSERT(fcookie);
|
||||
ASSERT(!fcookie->is_dir);
|
||||
|
||||
return fcookie->fscookie->fs->ops->seek(fcookie->ctxt, offset, whence);
|
||||
}
|
||||
|
||||
int fs_close(int handle)
|
||||
{
|
||||
struct filecookie *fcookie = fhtocp(handle);
|
||||
|
||||
ASSERT(fcookie);
|
||||
ASSERT(!fcookie->is_dir);
|
||||
ASSERT(fcookie->fscookie != NULL);
|
||||
ASSERT(fcookie->fscookie->fs != NULL);
|
||||
ASSERT(fcookie->fscookie->fs->ops != NULL);
|
||||
ASSERT(fcookie->fscookie->fs->ops->close != NULL);
|
||||
|
||||
fcookie->fscookie->fs->ops->close(fcookie->ctxt);
|
||||
|
||||
list_delete(&fcookie->node);
|
||||
free(fcookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fs_opendir(const char *path)
|
||||
{
|
||||
struct fsmount *mount;
|
||||
void *ctxt;
|
||||
struct filecookie *fcookie;
|
||||
const char *shortpath;
|
||||
int fd;
|
||||
|
||||
mount = path_to_mount(path, &shortpath);
|
||||
if (!mount)
|
||||
return -1;
|
||||
|
||||
if (-1 == (fd = getfd()))
|
||||
return -1;
|
||||
|
||||
ctxt = mount->cookie->fs->ops->opendir(mount->cookie->ctxt, shortpath);
|
||||
if (!ctxt)
|
||||
return -1;
|
||||
|
||||
fcookie = malloc(sizeof(struct filecookie));
|
||||
|
||||
fcookie->fscookie = mount->cookie;
|
||||
fcookie->ctxt = ctxt;
|
||||
fcookie->is_dir = true;
|
||||
fcookie->fd = fd;
|
||||
|
||||
list_add_head(&files, &fcookie->node);
|
||||
|
||||
return fcookie->fd;
|
||||
}
|
||||
|
||||
int fs_readdir(int handle, struct dirent *ent)
|
||||
{
|
||||
struct filecookie *fcookie = fhtocp(handle);
|
||||
|
||||
RELEASE_ASSERT(fcookie);
|
||||
RELEASE_ASSERT(fcookie->is_dir);
|
||||
|
||||
return fcookie->fscookie->fs->ops->readdir(fcookie->ctxt, ent);
|
||||
}
|
||||
|
||||
int fs_rewinddir(int handle)
|
||||
{
|
||||
struct filecookie *fcookie = fhtocp(handle);
|
||||
|
||||
RELEASE_ASSERT(fcookie);
|
||||
RELEASE_ASSERT(fcookie->is_dir);
|
||||
|
||||
fcookie->fscookie->fs->ops->rewinddir(fcookie->ctxt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fs_closedir(int handle)
|
||||
{
|
||||
struct filecookie *fcookie = fhtocp(handle);
|
||||
|
||||
RELEASE_ASSERT(fcookie);
|
||||
RELEASE_ASSERT(fcookie->is_dir);
|
||||
|
||||
fcookie->fscookie->fs->ops->closedir(fcookie->ctxt);
|
||||
|
||||
list_delete(&fcookie->node);
|
||||
free(fcookie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fs_load_file(const char *path, addr_t addr, size_t *maxsize)
|
||||
{
|
||||
int filefd;
|
||||
int result;
|
||||
struct stat stat;
|
||||
|
||||
RELEASE_ASSERT(maxsize != NULL);
|
||||
|
||||
/* open the file */
|
||||
filefd = fs_open(path, 0);
|
||||
if (filefd < 0) {
|
||||
platform_record_breadcrumb("fs_load_file", "fs_open_failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* stat it */
|
||||
fs_stat(filefd, &stat);
|
||||
|
||||
/* make sure it is not too large */
|
||||
if (*maxsize < stat.st_size) {
|
||||
platform_record_breadcrumb("fs_load_file", "maxsize_exceeded");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!security_allow_memory((void *)addr, stat.st_size)) {
|
||||
platform_record_breadcrumb("fs_load_file", "permission_denied");
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read the entire thing in */
|
||||
result = fs_read(filefd, (void *)addr, stat.st_size);
|
||||
fs_close(filefd);
|
||||
|
||||
if (result < 0 || (unsigned)result != stat.st_size) {
|
||||
platform_record_breadcrumb("fs_load_file", "read_failed");
|
||||
// Zero the buffer in case of a partial read
|
||||
memset((void *)addr, *maxsize, 0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
*maxsize = stat.st_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fs_dump_mounts(void)
|
||||
{
|
||||
struct fsmount *mount;
|
||||
|
||||
printf("active fs mounts:\n");
|
||||
list_for_every_entry(&mounts, mount, struct fsmount, node) {
|
||||
printf("\t'%s'\n", mount->mountpoint);
|
||||
}
|
||||
}
|
||||
|
||||
const char *fs_get_fsname(const int index)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < index; i++)
|
||||
if (fs_list[i].name == NULL)
|
||||
return(NULL);
|
||||
return(fs_list[i].name);
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2007-2011 Apple Inc. All rights reserved.
|
||||
* Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* The contents of this file constitute Original Code as defined in and
|
||||
* are subject to the Apple Public Source License Version 1.1 (the
|
||||
* "License"). You may not use this file except in compliance with the
|
||||
* License. Please obtain a copy of the License at
|
||||
* http://www.apple.com/publicsource and read it before using this file.
|
||||
*
|
||||
* This Original Code and all software distributed under the License are
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
/*
|
||||
* cache.c - A simple cache for file systems meta-data.
|
||||
*
|
||||
* Copyright (c) 2000 - 2003 Apple Computer, Inc.
|
||||
*
|
||||
* DRI: Josh de Cesare
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hfs.h"
|
||||
|
||||
struct CacheEntry {
|
||||
CICell ih;
|
||||
time_t time;
|
||||
off_t offset;
|
||||
};
|
||||
typedef struct CacheEntry CacheEntry;
|
||||
|
||||
#define kCacheSize (kFSCacheSize)
|
||||
#define kCacheMinBlockSize (0x200)
|
||||
#define kCacheMaxBlockSize (0x4000)
|
||||
#define kCacheMaxEntries (kCacheSize / kCacheMinBlockSize)
|
||||
|
||||
static CICell gCacheIH;
|
||||
static uint32_t gCacheBlockSize;
|
||||
static uint32_t gCacheNumEntries;
|
||||
static time_t gCacheTime;
|
||||
static CacheEntry gCacheEntries[kCacheMaxEntries];
|
||||
static uint8_t *gCacheBuffer;
|
||||
|
||||
uint32_t gCacheHits;
|
||||
uint32_t gCacheMisses;
|
||||
uint32_t gCacheEvicts;
|
||||
|
||||
void CacheInit(CICell ih, uint32_t blockSize)
|
||||
{
|
||||
if ((blockSize < kCacheMinBlockSize) ||
|
||||
(blockSize >= kCacheMaxBlockSize))
|
||||
return;
|
||||
|
||||
if (gCacheBuffer)
|
||||
free(gCacheBuffer);
|
||||
posix_memalign((void **)&gCacheBuffer, blockSize, kFSCacheSize);
|
||||
|
||||
gCacheBlockSize = blockSize;
|
||||
gCacheNumEntries = kCacheSize / gCacheBlockSize;
|
||||
gCacheTime = 0;
|
||||
|
||||
gCacheHits = 0;
|
||||
gCacheMisses = 0;
|
||||
gCacheEvicts = 0;
|
||||
|
||||
bzero(gCacheEntries, sizeof(gCacheEntries));
|
||||
|
||||
gCacheIH = ih;
|
||||
}
|
||||
|
||||
|
||||
uint32_t CacheRead(CICell ih, uint8_t *buffer, off_t offset,
|
||||
uint32_t length, bool cache)
|
||||
{
|
||||
uint32_t cnt, oldestEntry = 0, loadCache = 0;
|
||||
uint32_t result;
|
||||
CacheEntry *entry;
|
||||
time_t oldestTime;
|
||||
|
||||
// See if the data can be cached.
|
||||
if (cache && (gCacheIH == ih) && (length == gCacheBlockSize)) {
|
||||
// Look for the data in the cache.
|
||||
for (cnt = 0; cnt < gCacheNumEntries; cnt++) {
|
||||
entry = &gCacheEntries[cnt];
|
||||
if ((entry->ih == ih) && (entry->offset == offset)) {
|
||||
entry->time = ++gCacheTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the data was found copy it to the caller.
|
||||
if (cnt != gCacheNumEntries) {
|
||||
memcpy(buffer, gCacheBuffer + cnt * gCacheBlockSize, gCacheBlockSize);
|
||||
gCacheHits++;
|
||||
return gCacheBlockSize;
|
||||
}
|
||||
|
||||
// Could not find the data in the cache.
|
||||
loadCache = 1;
|
||||
}
|
||||
|
||||
// Read the data from the disk.
|
||||
// <rdar://problem/12080714> CacheRead (in HFS) should return actual amount read
|
||||
result = (uint32_t) HFSBlockRead(ih, (CICell)buffer, offset, length);
|
||||
if (cache) gCacheMisses++;
|
||||
|
||||
// Put the data from the disk in the cache if needed and if read completed
|
||||
// successfully
|
||||
if (loadCache && (result == length)) {
|
||||
// Find a free entry.
|
||||
oldestTime = gCacheTime;
|
||||
for (cnt = 0; cnt < gCacheNumEntries; cnt++) {
|
||||
entry = &gCacheEntries[cnt];
|
||||
|
||||
// Found a free entry.
|
||||
if (entry->ih == 0) break;
|
||||
|
||||
if (entry->time < oldestTime) {
|
||||
oldestTime = entry->time;
|
||||
oldestEntry = cnt;
|
||||
}
|
||||
}
|
||||
|
||||
// If no free entry was found, use the oldest.
|
||||
if (cnt == gCacheNumEntries) {
|
||||
cnt = oldestEntry;
|
||||
gCacheEvicts++;
|
||||
}
|
||||
|
||||
// Copy the data from disk to the new entry.
|
||||
entry = &gCacheEntries[cnt];
|
||||
entry->ih = ih;
|
||||
entry->time = ++gCacheTime;
|
||||
entry->offset = offset;
|
||||
memcpy(gCacheBuffer + cnt * gCacheBlockSize, buffer, gCacheBlockSize);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#include <stdint.h>
|
||||
#include <unittest.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <lib/fs.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct blockdev *create_mock_blockdev(const char *name, const char *filename, uint32_t block_size);
|
||||
|
||||
void walk(const char *dirpath)
|
||||
{
|
||||
int dirfd;
|
||||
struct dirent ent;
|
||||
int result;
|
||||
|
||||
dirfd = fs_opendir(dirpath);
|
||||
if (dirfd < 0) {
|
||||
printf("error opening dir '%s'\n", dirpath);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((result = fs_readdir(dirfd, &ent)) >= 0) {
|
||||
size_t subpath_size = strlen(dirpath) + 1 + strlen(ent.d_name) + 1;
|
||||
char *subpath = malloc(subpath_size);
|
||||
snprintf(subpath, subpath_size, "%s/%s", dirpath, ent.d_name);
|
||||
if (ent.d_type == FILE_TYPE_FILE) {
|
||||
int fd;
|
||||
printf("%s\n", subpath);
|
||||
|
||||
fd = fs_open(subpath, 0);
|
||||
if (fd >= 0) {
|
||||
void *buffer;
|
||||
struct stat st;
|
||||
if (fs_stat(fd, &st) >= 0) {
|
||||
unsigned size = st.st_size;
|
||||
if (size > 1000000)
|
||||
size = 1000000;
|
||||
buffer = malloc(size);
|
||||
if (fs_read(fd, buffer, size) < 0) {
|
||||
printf("error reading file '%s'\n", subpath);
|
||||
}
|
||||
free(buffer);
|
||||
} else {
|
||||
printf("error statting file '%s'\n", subpath);
|
||||
}
|
||||
} else {
|
||||
printf("error opening file '%s'\n", subpath);
|
||||
}
|
||||
} else if (ent.d_type == FILE_TYPE_DIR) {
|
||||
printf("%s/\n", subpath);
|
||||
walk(subpath);
|
||||
}
|
||||
|
||||
free(subpath);
|
||||
}
|
||||
fs_closedir(dirfd);
|
||||
}
|
||||
|
||||
int fuzz_main(const char *filename)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
int result;
|
||||
|
||||
bdev = create_mock_blockdev("dev", filename, 4096);
|
||||
register_blockdev(bdev);
|
||||
|
||||
result = fs_mount("dev", "hfs", "/boot");
|
||||
|
||||
if (result == 0) {
|
||||
printf("Mount succeeded\n");
|
||||
walk("/boot");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mock
|
||||
bool
|
||||
env_blacklist(const char *name, bool write)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mock
|
||||
bool
|
||||
env_blacklist_nvram(const char *name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
+1372
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* The contents of this file constitute Original Code as defined in and
|
||||
* are subject to the Apple Public Source License Version 1.1 (the
|
||||
* "License"). You may not use this file except in compliance with the
|
||||
* License. Please obtain a copy of the License at
|
||||
* http://www.apple.com/publicsource and read it before using this file.
|
||||
*
|
||||
* This Original Code and all software distributed under the License are
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
/* Derived from: */
|
||||
/*
|
||||
* fs.h - Externs for the File System Modules
|
||||
*
|
||||
* Copyright (c) 1999-2004 Apple Computer, Inc.
|
||||
*
|
||||
* DRI: Josh de Cesare
|
||||
*/
|
||||
|
||||
#ifndef _BOOTX_FS_H_
|
||||
#define _BOOTX_FS_H_
|
||||
|
||||
// tunable
|
||||
#define kFSCacheSize (64 * 1024)
|
||||
|
||||
// File Permissions and Types
|
||||
enum {
|
||||
kPermOtherExecute = 1 << 0,
|
||||
kPermOtherWrite = 1 << 1,
|
||||
kPermOtherRead = 1 << 2,
|
||||
kPermGroupExecute = 1 << 3,
|
||||
kPermGroupWrite = 1 << 4,
|
||||
kPermGroupRead = 1 << 5,
|
||||
kPermOwnerExecute = 1 << 6,
|
||||
kPermOwnerWrite = 1 << 7,
|
||||
kPermOwnerRead = 1 << 8,
|
||||
kPermMask = 0x1FF,
|
||||
kOwnerNotRoot = 1 << 9,
|
||||
kFileTypeUnknown = 0x0 << 16,
|
||||
kFileTypeFlat = 0x1 << 16,
|
||||
kFileTypeDirectory = 0x2 << 16,
|
||||
kFileTypeLink = 0x3 << 16,
|
||||
kFileTypeMask = 0x3 << 16
|
||||
};
|
||||
|
||||
typedef void *CICell;
|
||||
|
||||
// Externs for cache.c
|
||||
extern uint32_t gCacheHits;
|
||||
extern uint32_t gCacheMisses;
|
||||
extern uint32_t gCacheEvicts;
|
||||
|
||||
extern void CacheInit(CICell ih, uint32_t chunkSize);
|
||||
extern uint32_t CacheRead(CICell ih, uint8_t *buffer, off_t offset,
|
||||
uint32_t length, bool cache);
|
||||
|
||||
// Externs for hfs.c
|
||||
extern int HFSInitPartition(CICell ih);
|
||||
extern int HFSDetect(void);
|
||||
extern int HFSReadFile(CICell ih, const char *filePath, void *base, off_t offset, size_t length);
|
||||
extern int HFSGetFileInfo(CICell ih, const char *filePath, uint32_t *flags, time_t *time, off_t *size);
|
||||
extern int HFSGetDirEntry(CICell ih, const char *dirPath, off_t *dirIndex, char *name, size_t nameSize, uint32_t *flags, time_t *time, off_t *size);
|
||||
extern struct HFSPlusVolumeHeader *HFSGetVolumeHeader(void);
|
||||
|
||||
// Externs for hfs_fs.c
|
||||
extern int HFSBlockRead(CICell *ih, void *buf, off_t offset, uint32_t length);
|
||||
|
||||
/*
|
||||
* We may need to work with big-endian data that's not
|
||||
* aligned. For ARM at least, this is an issue. Rather
|
||||
* than use swap functions and macrotize, these are explicitly
|
||||
* called out to avoid alignment issues even on big-endian systems.
|
||||
*
|
||||
* No these aren't efficient. No, it doesn't really matter...
|
||||
*/
|
||||
static inline u_int64_t
|
||||
HFSntohll(const void *ov)
|
||||
{
|
||||
unsigned const char *ovb = (unsigned const char *)ov;
|
||||
|
||||
return(((u_int64_t)ovb[0] << 56) +
|
||||
((u_int64_t)ovb[1] << 48) +
|
||||
((u_int64_t)ovb[2] << 40) +
|
||||
((u_int64_t)ovb[3] << 32) +
|
||||
((u_int64_t)ovb[4] << 24) +
|
||||
((u_int64_t)ovb[5] << 16) +
|
||||
((u_int64_t)ovb[6] << 8) +
|
||||
(u_int64_t)ovb[7]);
|
||||
}
|
||||
|
||||
static inline u_int32_t
|
||||
HFSntohl(const void *ov)
|
||||
{
|
||||
unsigned const char *ovb = (unsigned const char *)ov;
|
||||
|
||||
return(((u_int32_t)ovb[0] << 24) +
|
||||
((u_int32_t)ovb[1] << 16) +
|
||||
((u_int32_t)ovb[2] << 8) +
|
||||
(u_int32_t)ovb[3]);
|
||||
}
|
||||
|
||||
static inline u_int16_t
|
||||
HFSntohs(const void *ov)
|
||||
{
|
||||
unsigned const char *ovb = (unsigned const char *)ov;
|
||||
|
||||
return(((u_int16_t)ovb[0] << 8) + (u_int16_t)ovb[1]);
|
||||
}
|
||||
|
||||
static inline short
|
||||
HFShtons(const unsigned short ov)
|
||||
{
|
||||
unsigned short nv;
|
||||
unsigned char *nvb = (unsigned char *)&nv;
|
||||
|
||||
nvb[0] = (ov >> 8);
|
||||
nvb[1] = ov & 0xff;
|
||||
|
||||
return(nv);
|
||||
}
|
||||
|
||||
|
||||
#endif /* ! _BOOTX_FS_H_ */
|
||||
@@ -0,0 +1,746 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2011 Apple Inc. All rights reserved.
|
||||
* Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
#ifndef __HFS_FORMAT__
|
||||
#define __HFS_FORMAT__
|
||||
|
||||
/*
|
||||
* hfs_format.c
|
||||
*
|
||||
* This file describes the on-disk format for HFS and HFS Plus volumes.
|
||||
* The HFS Plus volume format is desciibed in detail in Apple Technote 1150.
|
||||
*
|
||||
* http://developer.apple.com/technotes/tn/tn1150.html
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* some on-disk hfs structures have 68K alignment (misaligned) */
|
||||
|
||||
/* Signatures used to differentiate between HFS and HFS Plus volumes */
|
||||
enum {
|
||||
kHFSSigWord = 0x4244, /* 'BD' in ASCII */
|
||||
kHFSPlusSigWord = 0x482B, /* 'H+' in ASCII */
|
||||
kHFSXSigWord = 0x4858, /* 'HX' in ASCII */
|
||||
|
||||
kHFSPlusVersion = 0x0004, /* 'H+' volumes are version 4 only */
|
||||
kHFSXVersion = 0x0005, /* 'HX' volumes start with version 5 */
|
||||
|
||||
kHFSPlusMountVersion = 0x31302E30, /* '10.0' for Mac OS X */
|
||||
kHFSJMountVersion = 0x4846534a, /* 'HFSJ' for journaled HFS+ on OS X */
|
||||
kFSKMountVersion = 0x46534b21 /* 'FSK!' for failed journal replay */
|
||||
};
|
||||
|
||||
|
||||
#ifdef __APPLE_API_PRIVATE
|
||||
/*
|
||||
* Mac OS X has two special directories on HFS+ volumes for hardlinked files
|
||||
* and hardlinked directories as well as for open-unlinked files.
|
||||
*
|
||||
* These directories and their contents are not exported from the filesystem
|
||||
* under Mac OS X.
|
||||
*/
|
||||
#define HFSPLUSMETADATAFOLDER "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data"
|
||||
#define HFSPLUS_ARCHIVED_FOLDERS ".Archived_Directories\x0d"
|
||||
|
||||
/*
|
||||
* Files in the "HFS+ Private Data" folder have one of the following prefixes
|
||||
* followed by a decimal number (no leading zeros) for the file ID.
|
||||
*
|
||||
* Note: Earlier version of Mac OS X used a 32 bit random number for the link
|
||||
* ref number instead of the file id.
|
||||
*
|
||||
* e.g. iNode7182000 and temp3296
|
||||
*/
|
||||
#define HFS_INODE_PREFIX "iNode"
|
||||
#define HFS_DELETE_PREFIX "temp"
|
||||
|
||||
/*
|
||||
* Files in the ".Archived_Directories" folder have the following prefix
|
||||
* followed by a decimal number (no leading zeros) for the file ID.
|
||||
*
|
||||
* e.g. dir_555
|
||||
*/
|
||||
#define HFS_DIRINODE_PREFIX "dir_"
|
||||
|
||||
/*
|
||||
* Hardlink inodes save the head of the link chain in
|
||||
* an extended attribute named FIRST_LINK_XATTR_NAME.
|
||||
* The attribute data is the decimal value in ASCII
|
||||
* of the cnid for the first link in the chain.
|
||||
*
|
||||
* This extended attribute is private (i.e. its not
|
||||
* exported in the getxattr/listxattr POSIX APIs).
|
||||
*/
|
||||
#define FIRST_LINK_XATTR_NAME "com.apple.system.hfs.firstlink"
|
||||
#define FIRST_LINK_XATTR_REC_SIZE (sizeof(HFSPlusAttrData) - 2 + 12)
|
||||
|
||||
#endif /* __APPLE_API_PRIVATE */
|
||||
|
||||
/*
|
||||
* Indirect link files (hard links) have the following type/creator.
|
||||
*/
|
||||
enum {
|
||||
kHardLinkFileType = 0x686C6E6B, /* 'hlnk' */
|
||||
kHFSPlusCreator = 0x6866732B /* 'hfs+' */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* File type and creator for symbolic links
|
||||
*/
|
||||
enum {
|
||||
kSymLinkFileType = 0x736C6E6B, /* 'slnk' */
|
||||
kSymLinkCreator = 0x72686170 /* 'rhap' */
|
||||
};
|
||||
|
||||
|
||||
#ifndef _HFSUNISTR255_DEFINED_
|
||||
#define _HFSUNISTR255_DEFINED_
|
||||
/* Unicode strings are used for HFS Plus file and folder names */
|
||||
struct HFSUniStr255 {
|
||||
uint16_t length; /* number of unicode characters */
|
||||
uint16_t unicode[255]; /* unicode characters */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSUniStr255 HFSUniStr255;
|
||||
typedef const HFSUniStr255 *ConstHFSUniStr255Param;
|
||||
#endif /* _HFSUNISTR255_DEFINED_ */
|
||||
|
||||
enum {
|
||||
kHFSMaxVolumeNameChars = 27,
|
||||
kHFSMaxFileNameChars = 31,
|
||||
kHFSPlusMaxFileNameChars = 255
|
||||
};
|
||||
|
||||
|
||||
/* Extent overflow file data structures */
|
||||
|
||||
/* HFS Extent key */
|
||||
struct HFSExtentKey {
|
||||
uint8_t keyLength; /* length of key, excluding this field */
|
||||
uint8_t forkType; /* 0 = data fork, FF = resource fork */
|
||||
uint32_t fileID; /* file ID */
|
||||
uint16_t startBlock; /* first file allocation block number in this extent */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSExtentKey HFSExtentKey;
|
||||
|
||||
/* HFS Plus Extent key */
|
||||
struct HFSPlusExtentKey {
|
||||
uint16_t keyLength; /* length of key, excluding this field */
|
||||
uint8_t forkType; /* 0 = data fork, FF = resource fork */
|
||||
uint8_t pad; /* make the other fields align on 32-bit boundary */
|
||||
uint32_t fileID; /* file ID */
|
||||
uint32_t startBlock; /* first file allocation block number in this extent */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusExtentKey HFSPlusExtentKey;
|
||||
|
||||
/* Number of extent descriptors per extent record */
|
||||
enum {
|
||||
kHFSExtentDensity = 3,
|
||||
kHFSPlusExtentDensity = 8
|
||||
};
|
||||
|
||||
/* HFS extent descriptor */
|
||||
struct HFSExtentDescriptor {
|
||||
uint16_t startBlock; /* first allocation block */
|
||||
uint16_t blockCount; /* number of allocation blocks */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSExtentDescriptor HFSExtentDescriptor;
|
||||
|
||||
/* HFS Plus extent descriptor */
|
||||
struct HFSPlusExtentDescriptor {
|
||||
uint32_t startBlock; /* first allocation block */
|
||||
uint32_t blockCount; /* number of allocation blocks */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusExtentDescriptor HFSPlusExtentDescriptor;
|
||||
|
||||
/* HFS extent record */
|
||||
typedef HFSExtentDescriptor HFSExtentRecord[3];
|
||||
|
||||
/* HFS Plus extent record */
|
||||
typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[8];
|
||||
|
||||
|
||||
/* Finder information */
|
||||
struct FndrFileInfo {
|
||||
uint32_t fdType; /* file type */
|
||||
uint32_t fdCreator; /* file creator */
|
||||
uint16_t fdFlags; /* Finder flags */
|
||||
struct {
|
||||
int16_t v; /* file's location */
|
||||
int16_t h;
|
||||
} fdLocation;
|
||||
int16_t opaque;
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct FndrFileInfo FndrFileInfo;
|
||||
|
||||
struct FndrDirInfo {
|
||||
struct { /* folder's window rectangle */
|
||||
int16_t top;
|
||||
int16_t left;
|
||||
int16_t bottom;
|
||||
int16_t right;
|
||||
} frRect;
|
||||
unsigned short frFlags; /* Finder flags */
|
||||
struct {
|
||||
uint16_t v; /* folder's location */
|
||||
uint16_t h;
|
||||
} frLocation;
|
||||
int16_t opaque;
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct FndrDirInfo FndrDirInfo;
|
||||
|
||||
struct FndrOpaqueInfo {
|
||||
int8_t opaque[16];
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct FndrOpaqueInfo FndrOpaqueInfo;
|
||||
|
||||
|
||||
/* HFS Plus Fork data info - 80 bytes */
|
||||
struct HFSPlusForkData {
|
||||
uint64_t logicalSize; /* fork's logical size in bytes */
|
||||
uint32_t clumpSize; /* fork's clump size in bytes */
|
||||
uint32_t totalBlocks; /* total blocks used by this fork */
|
||||
HFSPlusExtentRecord extents; /* initial set of extents */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusForkData HFSPlusForkData;
|
||||
|
||||
|
||||
/* Mac OS X has 16 bytes worth of "BSD" info.
|
||||
*
|
||||
* Note: Mac OS 9 implementations and applications
|
||||
* should preserve, but not change, this information.
|
||||
*/
|
||||
struct HFSPlusBSDInfo {
|
||||
uint32_t ownerID; /* user-id of owner or hard link chain previous link */
|
||||
uint32_t groupID; /* group-id of owner or hard link chain next link */
|
||||
uint8_t adminFlags; /* super-user changeable flags */
|
||||
uint8_t ownerFlags; /* owner changeable flags */
|
||||
uint16_t fileMode; /* file type and permission bits */
|
||||
union {
|
||||
uint32_t iNodeNum; /* indirect node number (hard links only) */
|
||||
uint32_t linkCount; /* links that refer to this indirect node */
|
||||
uint32_t rawDevice; /* special file device (FBLK and FCHR only) */
|
||||
} special;
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusBSDInfo HFSPlusBSDInfo;
|
||||
|
||||
/*
|
||||
* Hardlink "links" resolve to an inode
|
||||
* and the actual uid/gid comes from that
|
||||
* inode.
|
||||
*
|
||||
* We repurpose the links's uid/gid fields
|
||||
* for the hardlink link chain. The chain
|
||||
* consists of a doubly linked list of file
|
||||
* ids.
|
||||
*/
|
||||
|
||||
#define hl_firstLinkID reserved1 /* Valid only if HasLinkChain flag is set (indirect nodes only) */
|
||||
|
||||
#define hl_prevLinkID bsdInfo.ownerID /* Valid only if HasLinkChain flag is set */
|
||||
#define hl_nextLinkID bsdInfo.groupID /* Valid only if HasLinkChain flag is set */
|
||||
|
||||
#define hl_linkReference bsdInfo.special.iNodeNum
|
||||
#define hl_linkCount bsdInfo.special.linkCount
|
||||
|
||||
|
||||
/* Catalog file data structures */
|
||||
|
||||
enum {
|
||||
kHFSRootParentID = 1, /* Parent ID of the root folder */
|
||||
kHFSRootFolderID = 2, /* Folder ID of the root folder */
|
||||
kHFSExtentsFileID = 3, /* File ID of the extents file */
|
||||
kHFSCatalogFileID = 4, /* File ID of the catalog file */
|
||||
kHFSBadBlockFileID = 5, /* File ID of the bad allocation block file */
|
||||
kHFSAllocationFileID = 6, /* File ID of the allocation file (HFS Plus only) */
|
||||
kHFSStartupFileID = 7, /* File ID of the startup file (HFS Plus only) */
|
||||
kHFSAttributesFileID = 8, /* File ID of the attribute file (HFS Plus only) */
|
||||
kHFSAttributeDataFileID = 13, /* Used in Mac OS X runtime for extent based attributes */
|
||||
/* kHFSAttributeDataFileID is never stored on disk. */
|
||||
kHFSRepairCatalogFileID = 14, /* Used when rebuilding Catalog B-tree */
|
||||
kHFSBogusExtentFileID = 15, /* Used for exchanging extents in extents file */
|
||||
kHFSFirstUserCatalogNodeID = 16
|
||||
};
|
||||
|
||||
/* HFS catalog key */
|
||||
struct HFSCatalogKey {
|
||||
uint8_t keyLength; /* key length (in bytes) */
|
||||
uint8_t reserved; /* reserved (set to zero) */
|
||||
uint32_t parentID; /* parent folder ID */
|
||||
uint8_t nodeName[kHFSMaxFileNameChars + 1]; /* catalog node name */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSCatalogKey HFSCatalogKey;
|
||||
|
||||
/* HFS Plus catalog key */
|
||||
struct HFSPlusCatalogKey {
|
||||
uint16_t keyLength; /* key length (in bytes) */
|
||||
uint32_t parentID; /* parent folder ID */
|
||||
HFSUniStr255 nodeName; /* catalog node name */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusCatalogKey HFSPlusCatalogKey;
|
||||
|
||||
/* Catalog record types */
|
||||
enum {
|
||||
/* HFS Catalog Records */
|
||||
kHFSFolderRecord = 0x0100, /* Folder record */
|
||||
kHFSFileRecord = 0x0200, /* File record */
|
||||
kHFSFolderThreadRecord = 0x0300, /* Folder thread record */
|
||||
kHFSFileThreadRecord = 0x0400, /* File thread record */
|
||||
|
||||
/* HFS Plus Catalog Records */
|
||||
kHFSPlusFolderRecord = 1, /* Folder record */
|
||||
kHFSPlusFileRecord = 2, /* File record */
|
||||
kHFSPlusFolderThreadRecord = 3, /* Folder thread record */
|
||||
kHFSPlusFileThreadRecord = 4 /* File thread record */
|
||||
};
|
||||
|
||||
|
||||
/* Catalog file record flags */
|
||||
enum {
|
||||
kHFSFileLockedBit = 0x0000, /* file is locked and cannot be written to */
|
||||
kHFSFileLockedMask = 0x0001,
|
||||
|
||||
kHFSThreadExistsBit = 0x0001, /* a file thread record exists for this file */
|
||||
kHFSThreadExistsMask = 0x0002,
|
||||
|
||||
kHFSHasAttributesBit = 0x0002, /* object has extended attributes */
|
||||
kHFSHasAttributesMask = 0x0004,
|
||||
|
||||
kHFSHasSecurityBit = 0x0003, /* object has security data (ACLs) */
|
||||
kHFSHasSecurityMask = 0x0008,
|
||||
|
||||
kHFSHasFolderCountBit = 0x0004, /* folder maintains a seperate sub-folder count */
|
||||
kHFSHasFolderCountMask = 0x0010,
|
||||
|
||||
kHFSHasLinkChainBit = 0x0005, /* has hardlink chain (inode or link) */
|
||||
kHFSHasLinkChainMask = 0x0020,
|
||||
|
||||
kHFSHasChildLinkBit = 0x0006, /* folder has a child that's a dir link */
|
||||
kHFSHasChildLinkMask = 0x0040
|
||||
};
|
||||
|
||||
|
||||
/* HFS catalog folder record - 70 bytes */
|
||||
struct HFSCatalogFolder {
|
||||
int16_t recordType; /* == kHFSFolderRecord */
|
||||
uint16_t flags; /* folder flags */
|
||||
uint16_t valence; /* folder valence */
|
||||
uint32_t folderID; /* folder ID */
|
||||
uint32_t createDate; /* date and time of creation */
|
||||
uint32_t modifyDate; /* date and time of last modification */
|
||||
uint32_t backupDate; /* date and time of last backup */
|
||||
FndrDirInfo userInfo; /* Finder information */
|
||||
FndrOpaqueInfo finderInfo; /* additional Finder information */
|
||||
uint32_t reserved[4]; /* reserved - initialized as zero */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSCatalogFolder HFSCatalogFolder;
|
||||
|
||||
/* HFS Plus catalog folder record - 88 bytes */
|
||||
struct HFSPlusCatalogFolder {
|
||||
int16_t recordType; /* == kHFSPlusFolderRecord */
|
||||
uint16_t flags; /* file flags */
|
||||
uint32_t valence; /* folder's item count */
|
||||
uint32_t folderID; /* folder ID */
|
||||
uint32_t createDate; /* date and time of creation */
|
||||
uint32_t contentModDate; /* date and time of last content modification */
|
||||
uint32_t attributeModDate; /* date and time of last attribute modification */
|
||||
uint32_t accessDate; /* date and time of last access (MacOS X only) */
|
||||
uint32_t backupDate; /* date and time of last backup */
|
||||
HFSPlusBSDInfo bsdInfo; /* permissions (for MacOS X) */
|
||||
FndrDirInfo userInfo; /* Finder information */
|
||||
FndrOpaqueInfo finderInfo; /* additional Finder information */
|
||||
uint32_t textEncoding; /* hint for name conversions */
|
||||
uint32_t folderCount; /* number of enclosed folders, active when HasFolderCount is set */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusCatalogFolder HFSPlusCatalogFolder;
|
||||
|
||||
/* HFS catalog file record - 102 bytes */
|
||||
struct HFSCatalogFile {
|
||||
int16_t recordType; /* == kHFSFileRecord */
|
||||
uint8_t flags; /* file flags */
|
||||
int8_t fileType; /* file type (unused ?) */
|
||||
FndrFileInfo userInfo; /* Finder information */
|
||||
uint32_t fileID; /* file ID */
|
||||
uint16_t dataStartBlock; /* not used - set to zero */
|
||||
int32_t dataLogicalSize; /* logical EOF of data fork */
|
||||
int32_t dataPhysicalSize; /* physical EOF of data fork */
|
||||
uint16_t rsrcStartBlock; /* not used - set to zero */
|
||||
int32_t rsrcLogicalSize; /* logical EOF of resource fork */
|
||||
int32_t rsrcPhysicalSize; /* physical EOF of resource fork */
|
||||
uint32_t createDate; /* date and time of creation */
|
||||
uint32_t modifyDate; /* date and time of last modification */
|
||||
uint32_t backupDate; /* date and time of last backup */
|
||||
FndrOpaqueInfo finderInfo; /* additional Finder information */
|
||||
uint16_t clumpSize; /* file clump size (not used) */
|
||||
HFSExtentRecord dataExtents; /* first data fork extent record */
|
||||
HFSExtentRecord rsrcExtents; /* first resource fork extent record */
|
||||
uint32_t reserved; /* reserved - initialized as zero */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSCatalogFile HFSCatalogFile;
|
||||
|
||||
/* HFS Plus catalog file record - 248 bytes */
|
||||
struct HFSPlusCatalogFile {
|
||||
int16_t recordType; /* == kHFSPlusFileRecord */
|
||||
uint16_t flags; /* file flags */
|
||||
uint32_t reserved1; /* reserved - initialized as zero */
|
||||
uint32_t fileID; /* file ID */
|
||||
uint32_t createDate; /* date and time of creation */
|
||||
uint32_t contentModDate; /* date and time of last content modification */
|
||||
uint32_t attributeModDate; /* date and time of last attribute modification */
|
||||
uint32_t accessDate; /* date and time of last access (MacOS X only) */
|
||||
uint32_t backupDate; /* date and time of last backup */
|
||||
HFSPlusBSDInfo bsdInfo; /* permissions (for MacOS X) */
|
||||
FndrFileInfo userInfo; /* Finder information */
|
||||
FndrOpaqueInfo finderInfo; /* additional Finder information */
|
||||
uint32_t textEncoding; /* hint for name conversions */
|
||||
uint32_t reserved2; /* reserved - initialized as zero */
|
||||
|
||||
/* Note: these start on double long (64 bit) boundry */
|
||||
HFSPlusForkData dataFork; /* size and block data for data fork */
|
||||
HFSPlusForkData resourceFork; /* size and block data for resource fork */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusCatalogFile HFSPlusCatalogFile;
|
||||
|
||||
/* HFS catalog thread record - 46 bytes */
|
||||
struct HFSCatalogThread {
|
||||
int16_t recordType; /* == kHFSFolderThreadRecord or kHFSFileThreadRecord */
|
||||
int32_t reserved[2]; /* reserved - initialized as zero */
|
||||
uint32_t parentID; /* parent ID for this catalog node */
|
||||
uint8_t nodeName[kHFSMaxFileNameChars + 1]; /* name of this catalog node */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSCatalogThread HFSCatalogThread;
|
||||
|
||||
/* HFS Plus catalog thread record -- 264 bytes */
|
||||
struct HFSPlusCatalogThread {
|
||||
int16_t recordType; /* == kHFSPlusFolderThreadRecord or kHFSPlusFileThreadRecord */
|
||||
int16_t reserved; /* reserved - initialized as zero */
|
||||
uint32_t parentID; /* parent ID for this catalog node */
|
||||
HFSUniStr255 nodeName; /* name of this catalog node (variable length) */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusCatalogThread HFSPlusCatalogThread;
|
||||
|
||||
#ifdef __APPLE_API_UNSTABLE
|
||||
/*
|
||||
These are the types of records in the attribute B-tree. The values were
|
||||
chosen so that they wouldn't conflict with the catalog record types.
|
||||
*/
|
||||
enum {
|
||||
kHFSPlusAttrInlineData = 0x10, /* attributes whose data fits in a b-tree node */
|
||||
kHFSPlusAttrForkData = 0x20, /* extent based attributes (data lives in extents) */
|
||||
kHFSPlusAttrExtents = 0x30 /* overflow extents for large attributes */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
HFSPlusAttrForkData
|
||||
For larger attributes, whose value is stored in allocation blocks.
|
||||
If the attribute has more than 8 extents, there will be additonal
|
||||
records (of type HFSPlusAttrExtents) for this attribute.
|
||||
*/
|
||||
struct HFSPlusAttrForkData {
|
||||
uint32_t recordType; /* == kHFSPlusAttrForkData*/
|
||||
uint32_t reserved;
|
||||
HFSPlusForkData theFork; /* size and first extents of value*/
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusAttrForkData HFSPlusAttrForkData;
|
||||
|
||||
/*
|
||||
HFSPlusAttrExtents
|
||||
This record contains information about overflow extents for large,
|
||||
fragmented attributes.
|
||||
*/
|
||||
struct HFSPlusAttrExtents {
|
||||
uint32_t recordType; /* == kHFSPlusAttrExtents*/
|
||||
uint32_t reserved;
|
||||
HFSPlusExtentRecord extents; /* additional extents*/
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusAttrExtents HFSPlusAttrExtents;
|
||||
|
||||
/*
|
||||
* Atrributes B-tree Data Record
|
||||
*
|
||||
* For small attributes, whose entire value is stored
|
||||
* within a single B-tree record.
|
||||
*/
|
||||
struct HFSPlusAttrData {
|
||||
uint32_t recordType; /* == kHFSPlusAttrInlineData */
|
||||
uint32_t reserved[2];
|
||||
uint32_t attrSize; /* size of attribute data in bytes */
|
||||
uint8_t attrData[2]; /* variable length */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusAttrData HFSPlusAttrData;
|
||||
|
||||
|
||||
/* HFSPlusAttrInlineData is obsolete use HFSPlusAttrData instead */
|
||||
struct HFSPlusAttrInlineData {
|
||||
uint32_t recordType;
|
||||
uint32_t reserved;
|
||||
uint32_t logicalSize;
|
||||
uint8_t userData[2];
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusAttrInlineData HFSPlusAttrInlineData;
|
||||
|
||||
|
||||
/* A generic Attribute Record*/
|
||||
union HFSPlusAttrRecord {
|
||||
uint32_t recordType;
|
||||
HFSPlusAttrInlineData inlineData; /* NOT USED */
|
||||
HFSPlusAttrData attrData;
|
||||
HFSPlusAttrForkData forkData;
|
||||
HFSPlusAttrExtents overflowExtents;
|
||||
};
|
||||
typedef union HFSPlusAttrRecord HFSPlusAttrRecord;
|
||||
|
||||
/* Attribute key */
|
||||
enum { kHFSMaxAttrNameLen = 127 };
|
||||
struct HFSPlusAttrKey {
|
||||
uint16_t keyLength; /* key length (in bytes) */
|
||||
uint16_t pad; /* set to zero */
|
||||
uint32_t fileID; /* file associated with attribute */
|
||||
uint32_t startBlock; /* first allocation block number for extents */
|
||||
uint16_t attrNameLen; /* number of unicode characters */
|
||||
uint16_t attrName[kHFSMaxAttrNameLen]; /* attribute name (Unicode) */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusAttrKey HFSPlusAttrKey;
|
||||
|
||||
#define kHFSPlusAttrKeyMaximumLength (sizeof(HFSPlusAttrKey) - sizeof(uint16_t))
|
||||
#define kHFSPlusAttrKeyMinimumLength (kHFSPlusAttrKeyMaximumLength - kHFSMaxAttrNameLen*sizeof(uint16_t))
|
||||
|
||||
#endif /* __APPLE_API_UNSTABLE */
|
||||
|
||||
|
||||
/* Key and node lengths */
|
||||
enum {
|
||||
kHFSPlusExtentKeyMaximumLength = sizeof(HFSPlusExtentKey) - sizeof(uint16_t),
|
||||
kHFSExtentKeyMaximumLength = sizeof(HFSExtentKey) - sizeof(uint8_t),
|
||||
kHFSPlusCatalogKeyMaximumLength = sizeof(HFSPlusCatalogKey) - sizeof(uint16_t),
|
||||
kHFSPlusCatalogKeyMinimumLength = kHFSPlusCatalogKeyMaximumLength - sizeof(HFSUniStr255) + sizeof(uint16_t),
|
||||
kHFSCatalogKeyMaximumLength = sizeof(HFSCatalogKey) - sizeof(uint8_t),
|
||||
kHFSCatalogKeyMinimumLength = kHFSCatalogKeyMaximumLength - (kHFSMaxFileNameChars + 1) + sizeof(uint8_t),
|
||||
kHFSPlusCatalogMinNodeSize = 4096,
|
||||
kHFSPlusExtentMinNodeSize = 512,
|
||||
kHFSPlusAttrMinNodeSize = 4096
|
||||
};
|
||||
|
||||
/* HFS and HFS Plus volume attribute bits */
|
||||
enum {
|
||||
/* Bits 0-6 are reserved (always cleared by MountVol call) */
|
||||
kHFSVolumeHardwareLockBit = 7, /* volume is locked by hardware */
|
||||
kHFSVolumeUnmountedBit = 8, /* volume was successfully unmounted */
|
||||
kHFSVolumeSparedBlocksBit = 9, /* volume has bad blocks spared */
|
||||
kHFSVolumeNoCacheRequiredBit = 10, /* don't cache volume blocks (i.e. RAM or ROM disk) */
|
||||
kHFSBootVolumeInconsistentBit = 11, /* boot volume is inconsistent (System 7.6 and later) */
|
||||
kHFSCatalogNodeIDsReusedBit = 12,
|
||||
kHFSVolumeJournaledBit = 13, /* this volume has a journal on it */
|
||||
kHFSVolumeInconsistentBit = 14, /* serious inconsistencies detected at runtime */
|
||||
kHFSVolumeSoftwareLockBit = 15, /* volume is locked by software */
|
||||
|
||||
kHFSVolumeHardwareLockMask = 1 << kHFSVolumeHardwareLockBit,
|
||||
kHFSVolumeUnmountedMask = 1 << kHFSVolumeUnmountedBit,
|
||||
kHFSVolumeSparedBlocksMask = 1 << kHFSVolumeSparedBlocksBit,
|
||||
kHFSVolumeNoCacheRequiredMask = 1 << kHFSVolumeNoCacheRequiredBit,
|
||||
kHFSBootVolumeInconsistentMask = 1 << kHFSBootVolumeInconsistentBit,
|
||||
kHFSCatalogNodeIDsReusedMask = 1 << kHFSCatalogNodeIDsReusedBit,
|
||||
kHFSVolumeJournaledMask = 1 << kHFSVolumeJournaledBit,
|
||||
kHFSVolumeInconsistentMask = 1 << kHFSVolumeInconsistentBit,
|
||||
kHFSVolumeSoftwareLockMask = 1 << kHFSVolumeSoftwareLockBit,
|
||||
kHFSMDBAttributesMask = 0x8380
|
||||
};
|
||||
|
||||
|
||||
/* HFS Master Directory Block - 162 bytes */
|
||||
/* Stored at sector #2 (3rd sector) and second-to-last sector. */
|
||||
struct HFSMasterDirectoryBlock {
|
||||
uint16_t drSigWord; /* == kHFSSigWord */
|
||||
uint32_t drCrDate; /* date and time of volume creation */
|
||||
uint32_t drLsMod; /* date and time of last modification */
|
||||
uint16_t drAtrb; /* volume attributes */
|
||||
uint16_t drNmFls; /* number of files in root folder */
|
||||
uint16_t drVBMSt; /* first block of volume bitmap */
|
||||
uint16_t drAllocPtr; /* start of next allocation search */
|
||||
uint16_t drNmAlBlks; /* number of allocation blocks in volume */
|
||||
uint32_t drAlBlkSiz; /* size (in bytes) of allocation blocks */
|
||||
uint32_t drClpSiz; /* default clump size */
|
||||
uint16_t drAlBlSt; /* first allocation block in volume */
|
||||
uint32_t drNxtCNID; /* next unused catalog node ID */
|
||||
uint16_t drFreeBks; /* number of unused allocation blocks */
|
||||
uint8_t drVN[kHFSMaxVolumeNameChars + 1]; /* volume name */
|
||||
uint32_t drVolBkUp; /* date and time of last backup */
|
||||
uint16_t drVSeqNum; /* volume backup sequence number */
|
||||
uint32_t drWrCnt; /* volume write count */
|
||||
uint32_t drXTClpSiz; /* clump size for extents overflow file */
|
||||
uint32_t drCTClpSiz; /* clump size for catalog file */
|
||||
uint16_t drNmRtDirs; /* number of directories in root folder */
|
||||
uint32_t drFilCnt; /* number of files in volume */
|
||||
uint32_t drDirCnt; /* number of directories in volume */
|
||||
uint32_t drFndrInfo[8]; /* information used by the Finder */
|
||||
uint16_t drEmbedSigWord; /* embedded volume signature (formerly drVCSize) */
|
||||
HFSExtentDescriptor drEmbedExtent; /* embedded volume location and size (formerly drVBMCSize and drCtlCSize) */
|
||||
uint32_t drXTFlSize; /* size of extents overflow file */
|
||||
HFSExtentRecord drXTExtRec; /* extent record for extents overflow file */
|
||||
uint32_t drCTFlSize; /* size of catalog file */
|
||||
HFSExtentRecord drCTExtRec; /* extent record for catalog file */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSMasterDirectoryBlock HFSMasterDirectoryBlock;
|
||||
|
||||
|
||||
#ifdef __APPLE_API_UNSTABLE
|
||||
#define SET_HFS_TEXT_ENCODING(hint) \
|
||||
(0x656e6300 | ((hint) & 0xff))
|
||||
#define GET_HFS_TEXT_ENCODING(hint) \
|
||||
(((hint) & 0xffffff00) == 0x656e6300 ? (hint) & 0x000000ff : 0xffffffffU)
|
||||
#endif /* __APPLE_API_UNSTABLE */
|
||||
|
||||
|
||||
/* HFS Plus Volume Header - 512 bytes */
|
||||
/* Stored at sector #2 (3rd sector) and second-to-last sector. */
|
||||
struct HFSPlusVolumeHeader {
|
||||
uint16_t signature; /* == kHFSPlusSigWord */
|
||||
uint16_t version; /* == kHFSPlusVersion */
|
||||
uint32_t attributes; /* volume attributes */
|
||||
uint32_t lastMountedVersion; /* implementation version which last mounted volume */
|
||||
uint32_t journalInfoBlock; /* block addr of journal info (if volume is journaled, zero otherwise) */
|
||||
|
||||
uint32_t createDate; /* date and time of volume creation */
|
||||
uint32_t modifyDate; /* date and time of last modification */
|
||||
uint32_t backupDate; /* date and time of last backup */
|
||||
uint32_t checkedDate; /* date and time of last disk check */
|
||||
|
||||
uint32_t fileCount; /* number of files in volume */
|
||||
uint32_t folderCount; /* number of directories in volume */
|
||||
|
||||
uint32_t blockSize; /* size (in bytes) of allocation blocks */
|
||||
uint32_t totalBlocks; /* number of allocation blocks in volume (includes this header and VBM*/
|
||||
uint32_t freeBlocks; /* number of unused allocation blocks */
|
||||
|
||||
uint32_t nextAllocation; /* start of next allocation search */
|
||||
uint32_t rsrcClumpSize; /* default resource fork clump size */
|
||||
uint32_t dataClumpSize; /* default data fork clump size */
|
||||
uint32_t nextCatalogID; /* next unused catalog node ID */
|
||||
|
||||
uint32_t writeCount; /* volume write count */
|
||||
uint64_t encodingsBitmap; /* which encodings have been use on this volume */
|
||||
|
||||
uint8_t finderInfo[32]; /* information used by the Finder */
|
||||
|
||||
HFSPlusForkData allocationFile; /* allocation bitmap file */
|
||||
HFSPlusForkData extentsFile; /* extents B-tree file */
|
||||
HFSPlusForkData catalogFile; /* catalog B-tree file */
|
||||
HFSPlusForkData attributesFile; /* extended attributes B-tree file */
|
||||
HFSPlusForkData startupFile; /* boot file (secondary loader) */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct HFSPlusVolumeHeader HFSPlusVolumeHeader;
|
||||
|
||||
|
||||
/* B-tree structures */
|
||||
|
||||
enum BTreeKeyLimits{
|
||||
kMaxKeyLength = 520
|
||||
};
|
||||
|
||||
union BTreeKey{
|
||||
uint8_t length8;
|
||||
uint16_t length16;
|
||||
uint8_t rawData [kMaxKeyLength+2];
|
||||
};
|
||||
typedef union BTreeKey BTreeKey;
|
||||
|
||||
/* BTNodeDescriptor -- Every B-tree node starts with these fields. */
|
||||
struct BTNodeDescriptor {
|
||||
uint32_t fLink; /* next node at this level*/
|
||||
uint32_t bLink; /* previous node at this level*/
|
||||
int8_t kind; /* kind of node (leaf, index, header, map)*/
|
||||
uint8_t height; /* zero for header, map; child is one more than parent*/
|
||||
uint16_t numRecords; /* number of records in this node*/
|
||||
uint16_t reserved; /* reserved - initialized as zero */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct BTNodeDescriptor BTNodeDescriptor;
|
||||
|
||||
/* Constants for BTNodeDescriptor kind */
|
||||
enum {
|
||||
kBTLeafNode = -1,
|
||||
kBTIndexNode = 0,
|
||||
kBTHeaderNode = 1,
|
||||
kBTMapNode = 2
|
||||
};
|
||||
|
||||
/* BTHeaderRec -- The first record of a B-tree header node */
|
||||
struct BTHeaderRec {
|
||||
uint16_t treeDepth; /* maximum height (usually leaf nodes) */
|
||||
uint32_t rootNode; /* node number of root node */
|
||||
uint32_t leafRecords; /* number of leaf records in all leaf nodes */
|
||||
uint32_t firstLeafNode; /* node number of first leaf node */
|
||||
uint32_t lastLeafNode; /* node number of last leaf node */
|
||||
uint16_t nodeSize; /* size of a node, in bytes */
|
||||
uint16_t maxKeyLength; /* reserved */
|
||||
uint32_t totalNodes; /* total number of nodes in tree */
|
||||
uint32_t freeNodes; /* number of unused (free) nodes in tree */
|
||||
uint16_t reserved1; /* unused */
|
||||
uint32_t clumpSize; /* reserved */
|
||||
uint8_t btreeType; /* reserved */
|
||||
uint8_t keyCompareType; /* Key string Comparison Type */
|
||||
uint32_t attributes; /* persistent attributes about the tree */
|
||||
uint32_t reserved3[16]; /* reserved */
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct BTHeaderRec BTHeaderRec;
|
||||
|
||||
/* Constants for BTHeaderRec attributes */
|
||||
enum {
|
||||
kBTBadCloseMask = 0x00000001, /* reserved */
|
||||
kBTBigKeysMask = 0x00000002, /* key length field is 16 bits */
|
||||
kBTVariableIndexKeysMask = 0x00000004 /* keys in index nodes are variable length */
|
||||
};
|
||||
|
||||
|
||||
/* Catalog Key Name Comparison Type */
|
||||
enum {
|
||||
kHFSCaseFolding = 0xCF, /* case folding (case-insensitive) */
|
||||
kHFSBinaryCompare = 0xBC /* binary compare (case-sensitive) */
|
||||
};
|
||||
|
||||
/* JournalInfoBlock - Structure that describes where our journal lives */
|
||||
struct JournalInfoBlock {
|
||||
uint32_t flags;
|
||||
uint32_t device_signature[8]; // signature used to locate our device.
|
||||
uint64_t offset; // byte offset to the journal on the device
|
||||
uint64_t size; // size in bytes of the journal
|
||||
uint32_t reserved[32];
|
||||
} __attribute__((aligned(2), packed));
|
||||
typedef struct JournalInfoBlock JournalInfoBlock;
|
||||
|
||||
enum {
|
||||
kJIJournalInFSMask = 0x00000001,
|
||||
kJIJournalOnOtherDeviceMask = 0x00000002,
|
||||
kJIJournalNeedInitMask = 0x00000004
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HFS_FORMAT__ */
|
||||
@@ -0,0 +1,385 @@
|
||||
/* Copyright 2007-2011 Apple Inc. All rights Reserved */
|
||||
/* Copyright 2006 Apple Computer Inc, All Rights Reserved */
|
||||
|
||||
/*
|
||||
* Interface shim between the iBoot fs/bdev layers and the HFS code.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/fs.h>
|
||||
#include <lib/blockdev.h>
|
||||
#ifndef HFS_UNITTEST
|
||||
#include <sys/task.h>
|
||||
#endif
|
||||
#include "hfs.h"
|
||||
#include "hfs_format.h"
|
||||
|
||||
#if 0
|
||||
# define HFSdebug(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args)
|
||||
#else
|
||||
# define HFSdebug(fmt, args...) do { } while (0)
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* fs interface
|
||||
*/
|
||||
|
||||
static int hfs_mount(void **ctxt, const char *devname);
|
||||
static void hfs_unmount(void *ctxt);
|
||||
static int hfs_fsstat(void *ctxt, struct statfs *stat);
|
||||
|
||||
static void *hfs_open(void *ctxt, const char *path, uint8_t flags);
|
||||
static int hfs_read(void *filectxt, void *buf, size_t len);
|
||||
static int hfs_fstat(void *filectxt, struct stat *st);
|
||||
static off_t hfs_seek(void *filectxt, off_t offset, int whence);
|
||||
static void hfs_close(void *filectxt);
|
||||
|
||||
static void *hfs_opendir(void *ctxt, const char *path);
|
||||
static int hfs_readdir(void *dirctxt, struct dirent *ent);
|
||||
static void hfs_rewinddir(void *dirctxt);
|
||||
static void hfs_closedir(void *dirctxt);
|
||||
|
||||
struct fs_ops hfs_fs_ops = {
|
||||
hfs_mount,
|
||||
hfs_unmount,
|
||||
hfs_fsstat,
|
||||
|
||||
hfs_open,
|
||||
hfs_read,
|
||||
hfs_fstat,
|
||||
hfs_seek,
|
||||
hfs_close,
|
||||
|
||||
hfs_opendir,
|
||||
hfs_readdir,
|
||||
hfs_rewinddir,
|
||||
hfs_closedir
|
||||
};
|
||||
|
||||
struct hfs_context {
|
||||
struct blockdev *dev;
|
||||
struct statfs s;
|
||||
uint32_t blocksize;
|
||||
};
|
||||
|
||||
struct hfs_openfile {
|
||||
struct hfs_context *fs;
|
||||
char *path;
|
||||
off_t pos;
|
||||
struct stat s;
|
||||
};
|
||||
|
||||
struct hfs_opendir {
|
||||
struct hfs_context *fs;
|
||||
char *path;
|
||||
off_t pos;
|
||||
};
|
||||
|
||||
/*
|
||||
* Locking. Since the HFS code is not re-entrant, we must ensure
|
||||
* that only one task calls into it at a time.
|
||||
*/
|
||||
#ifndef HFS_UNITTEST
|
||||
static struct task *hfs_owner_task;
|
||||
|
||||
#define LOCK() \
|
||||
do { \
|
||||
while (hfs_owner_task != NULL) \
|
||||
task_yield(); \
|
||||
hfs_owner_task = current_task; \
|
||||
} while(0);
|
||||
|
||||
#define UNLOCK() \
|
||||
do { \
|
||||
hfs_owner_task = NULL; \
|
||||
} while(0);
|
||||
#else
|
||||
#define LOCK() do {} while(0)
|
||||
#define UNLOCK() do {} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static int
|
||||
hfs_mount(void **ctxt, const char *devname)
|
||||
{
|
||||
struct hfs_context *cp;
|
||||
struct HFSPlusVolumeHeader *vh;
|
||||
int result;
|
||||
|
||||
HFSdebug("mounting '%s'", devname);
|
||||
result = -1;
|
||||
|
||||
LOCK();
|
||||
cp = malloc(sizeof(struct hfs_context));
|
||||
|
||||
// find the device
|
||||
if ((cp->dev = lookup_blockdev(devname)) == NULL)
|
||||
goto out;
|
||||
|
||||
// make sure it's something we recognise
|
||||
if (HFSInitPartition(cp))
|
||||
goto out;
|
||||
|
||||
// HFSDetect();
|
||||
|
||||
// save FS stat info while we're at it
|
||||
vh = HFSGetVolumeHeader();
|
||||
cp->s.f_bsize = vh->blockSize;
|
||||
cp->s.f_blocks = vh->totalBlocks;
|
||||
cp->s.f_bfree = vh->freeBlocks;
|
||||
|
||||
result = 0;
|
||||
out:
|
||||
if (result) {
|
||||
free(cp);
|
||||
HFSdebug("mount failed");
|
||||
} else {
|
||||
HFSdebug("mounted fs %p", cp);
|
||||
*ctxt = cp;
|
||||
}
|
||||
UNLOCK();
|
||||
return(result);
|
||||
}
|
||||
|
||||
static void
|
||||
hfs_unmount(void *ctxt)
|
||||
{
|
||||
free(ctxt);
|
||||
}
|
||||
|
||||
static int
|
||||
hfs_fsstat(void *ctxt, struct statfs *stat)
|
||||
{
|
||||
struct hfs_context *cp = (struct hfs_context *)ctxt;
|
||||
|
||||
*stat = cp->s;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void *
|
||||
hfs_open(void *ctxt, const char *path, uint8_t open_flags)
|
||||
{
|
||||
struct hfs_context *cp = (struct hfs_context *)ctxt;
|
||||
struct hfs_openfile *of;
|
||||
uint32_t flags;
|
||||
uint32_t path_length;
|
||||
time_t time;
|
||||
off_t size;
|
||||
|
||||
HFSdebug("%s: openfile", path);
|
||||
if (path[0] == '/')
|
||||
path++;
|
||||
of = NULL;
|
||||
LOCK();
|
||||
|
||||
// check for file existence
|
||||
if (HFSGetFileInfo(cp, path, &flags, &time, &size))
|
||||
goto out;
|
||||
if ((flags & kFileTypeMask) != kFileTypeFlat)
|
||||
goto out;
|
||||
|
||||
// alloc file and populate path
|
||||
path_length = strlen(path) + 1;
|
||||
of = malloc(sizeof(struct hfs_openfile) + path_length);
|
||||
of->path = (char *)(of + 1);
|
||||
strlcpy(of->path, path, path_length);
|
||||
of->pos = 0;
|
||||
of->fs = cp;
|
||||
|
||||
// Since we have stat information courtesy of checking the file's existence,
|
||||
// save it away for the inevitable stat.
|
||||
of->s.st_ino = 0; // XXX
|
||||
of->s.st_size = size;
|
||||
of->s.st_blocks = size / cp->s.f_bsize + ((size % cp->s.f_bsize) ? 1 : 0);
|
||||
of->s.st_type = FILE_TYPE_FILE;
|
||||
of->s.st_flags = 0; // XXX
|
||||
of->s.st_ctime = of->s.st_mtime = of->s.st_atime = time;
|
||||
out:
|
||||
UNLOCK();
|
||||
if (of == NULL)
|
||||
HFSdebug("openfile failed");
|
||||
|
||||
return(of);
|
||||
}
|
||||
|
||||
static int
|
||||
hfs_read(void *filectxt, void *buf, size_t len)
|
||||
{
|
||||
struct hfs_openfile *of = (struct hfs_openfile *)filectxt;
|
||||
int result;
|
||||
|
||||
if (of->s.st_type != FILE_TYPE_FILE)
|
||||
return(-1);
|
||||
|
||||
HFSdebug("%s: read %zu bytes to %p", of->path, len, buf);
|
||||
|
||||
result = HFSReadFile(of->fs, of->path, buf, of->pos, len);
|
||||
|
||||
if (result)
|
||||
HFSdebug("read returns %d", result);
|
||||
|
||||
if (result > 0)
|
||||
of->pos += result;
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
static int
|
||||
hfs_fstat(void *filectxt, struct stat *st)
|
||||
{
|
||||
struct hfs_openfile *of = (struct hfs_openfile *)filectxt;
|
||||
|
||||
*st = of->s;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static off_t
|
||||
hfs_seek(void *filectxt, off_t offset, int whence)
|
||||
{
|
||||
struct hfs_openfile *of = (struct hfs_openfile *)filectxt;
|
||||
off_t newpos;
|
||||
|
||||
switch(whence) {
|
||||
case FILE_SEEK_SET:
|
||||
newpos = offset;
|
||||
break;
|
||||
|
||||
case FILE_SEEK_CUR:
|
||||
newpos = of->pos + offset;
|
||||
break;
|
||||
|
||||
case FILE_SEEK_END:
|
||||
newpos = of->s.st_size + offset;
|
||||
break;
|
||||
default:
|
||||
return(-1);
|
||||
}
|
||||
if ((newpos < 0) || (newpos > of->s.st_size))
|
||||
return(-1);
|
||||
|
||||
HFSdebug("%s: seek to 0x%llx", of->path, newpos);
|
||||
return((of->pos = newpos));
|
||||
}
|
||||
|
||||
static void
|
||||
hfs_close(void *filectxt)
|
||||
{
|
||||
free(filectxt);
|
||||
}
|
||||
|
||||
static void *
|
||||
hfs_opendir(void *ctxt, const char *path)
|
||||
{
|
||||
struct hfs_context *cp = (struct hfs_context *)ctxt;
|
||||
struct hfs_opendir *od;
|
||||
uint32_t flags;
|
||||
uint32_t path_length;
|
||||
|
||||
HFSdebug("%s: opendir", path);
|
||||
od = NULL;
|
||||
if (path[0] == '/')
|
||||
path++;
|
||||
LOCK();
|
||||
|
||||
// check for directory existence (/ always exists)
|
||||
if (path[0] != '\0') {
|
||||
if (HFSGetFileInfo(cp, path, &flags, 0, 0)) {
|
||||
HFSdebug("%s: can't get directory info", path);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
// alloc file and populate path
|
||||
path_length = strlen(path) + 1;
|
||||
od = malloc(sizeof(struct hfs_opendir) + path_length);
|
||||
od->path = (char *)(od + 1);
|
||||
strlcpy(od->path, path, path_length);
|
||||
od->pos = 0;
|
||||
od->fs = cp;
|
||||
out:
|
||||
UNLOCK();
|
||||
if (od == NULL) {
|
||||
HFSdebug("opendir failed");
|
||||
} else {
|
||||
HFSdebug("opendir OK");
|
||||
}
|
||||
return(od);
|
||||
}
|
||||
|
||||
static int
|
||||
hfs_readdir(void *dirctxt, struct dirent *ent)
|
||||
{
|
||||
struct hfs_opendir *od = (struct hfs_opendir *)dirctxt;
|
||||
uint32_t flags;
|
||||
time_t time;
|
||||
off_t size;
|
||||
int result;
|
||||
|
||||
if (od->pos == -1)
|
||||
return(-1); // end of directory
|
||||
HFSdebug("%s: readdir at 0x%llx", od->path, od->pos);
|
||||
|
||||
LOCK();
|
||||
if (HFSGetDirEntry(od->fs, od->path, &od->pos, ent->d_name, sizeof(ent->d_name), &flags, &time, &size) >= 0) {
|
||||
|
||||
ent->d_fileno = od->pos; // kinda dubious
|
||||
RELEASE_ASSERT(size <= INT32_MAX);
|
||||
ent->d_reclen = size; // ?! seems to be what it's for
|
||||
switch(flags & kFileTypeMask) {
|
||||
case kFileTypeFlat:
|
||||
ent->d_type = FILE_TYPE_FILE;
|
||||
break;
|
||||
case kFileTypeDirectory:
|
||||
ent->d_type = FILE_TYPE_DIR;
|
||||
break;
|
||||
default:
|
||||
ent->d_type = FILE_TYPE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
ent->d_name[sizeof(ent->d_name) - 1] = '\0';
|
||||
ent->d_namlen = strlen(ent->d_name);
|
||||
ent->d_ctime = ent->d_atime = ent->d_atime = time;
|
||||
result = 0;
|
||||
} else {
|
||||
result = -1;
|
||||
}
|
||||
UNLOCK();
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
static void
|
||||
hfs_rewinddir(void *dirctxt)
|
||||
{
|
||||
struct hfs_opendir *od = (struct hfs_opendir *)dirctxt;
|
||||
|
||||
od->pos = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hfs_closedir(void *dirctxt)
|
||||
{
|
||||
free(dirctxt);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* bdev interface
|
||||
*
|
||||
* Note that the blockdev layer deblocks for us, so this interface is trivial.
|
||||
*/
|
||||
|
||||
int
|
||||
HFSBlockRead(CICell *ih, void *buf, off_t offset, uint32_t length)
|
||||
{
|
||||
struct hfs_context *cp = (struct hfs_context *)ih;
|
||||
|
||||
HFSdebug("HFS physical read from 0x%llx/0x%x to %p", (unsigned long long)offset, length, buf);
|
||||
|
||||
return(blockdev_read(cp->dev, buf, offset, length));
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LIBHFS_DIR := $(GET_LOCAL_DIR)
|
||||
LIBHFS_BUILD := $(call TOLIBDIR,$(LIBHFS_DIR)/LIBHFS.a)
|
||||
COMMONLIBS += LIBHFS
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
GLOBAL_INCLUDES += $(SRCROOT)/arch/arm/include
|
||||
|
||||
LIBHFS_OBJS := \
|
||||
$(LIBHFS_DIR)/cache.o \
|
||||
$(LIBHFS_DIR)/hfs.o \
|
||||
$(LIBHFS_DIR)/hfs_fs.o
|
||||
|
||||
LIBHFS_OBJS := $(call TOLIBOBJDIR,$(LIBHFS_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBHFS_OBJS:%o=%d)
|
||||
|
||||
$(LIBHFS_BUILD): $(LIBHFS_OBJS)
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2008, 2014 Apple Inc. All rights reserved.
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
OPTIONS += WITH_HFS=1
|
||||
MODULES += lib/fs
|
||||
LIBRARY_MODULES += lib/fs \
|
||||
lib/fs/hfs
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Return an error if any commands below fail
|
||||
set -e
|
||||
|
||||
echo "Creating test filesystem"
|
||||
|
||||
HFS_TEMPDIR=$(mktemp -d -t hfstest)
|
||||
|
||||
TESTFS_ROOT=${HFS_TEMPDIR}/testfs_root
|
||||
export TESTFS_COMPARE_ROOT=${HFS_TEMPDIR}/testfs_compare
|
||||
export TESTFS_IMAGE=${HFS_TEMPDIR}/testfs.dmg
|
||||
|
||||
DATA_PATTERN=evYya7KUSI1y5Tc05IQftxDuoIz8SlIRHDQLTA
|
||||
|
||||
mkdir $TESTFS_ROOT
|
||||
|
||||
for i in a a/a bb bb/bb-a bb/bb-b ccc dddd eeeee ffffff ggggggg hhhhhhhh iiiiiiiii jjjjjjjjjj kkkkkkkkkkk lllllllllllllllllllllllllllllllllllll m n o p q dir; do mkdir -p ${TESTFS_ROOT}/$i; done
|
||||
|
||||
dd if=/dev/zero of=${TESTFS_ROOT}/dir/testfile0 bs=1 count=0
|
||||
dd if=/dev/zero of=${TESTFS_ROOT}/dir/testfile1 bs=1 count=1
|
||||
dd if=/dev/zero of=${TESTFS_ROOT}/dir/testfile2 bs=1 count=2
|
||||
dd if=/dev/zero of=${TESTFS_ROOT}/dir/testfile512 bs=1 count=512
|
||||
dd if=/dev/zero of=${TESTFS_ROOT}/dir/testfile1024 bs=1 count=1024
|
||||
for i in {1..1024}; do echo ${DATA_PATTERN} >> ${TESTFS_ROOT}/dir/testfile-${DATA_PATTERN}; done
|
||||
|
||||
echo "Creating disk image for test filesystem"
|
||||
|
||||
hdiutil create -size 4m -srcfolder $TESTFS_ROOT -format UDRW -fs HFSX -layout NONE $TESTFS_IMAGE
|
||||
|
||||
./fs
|
||||
@@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_OBJS := \
|
||||
lib/libc/log2.o \
|
||||
lib/blockdev/blockdev.o \
|
||||
lib/blockdev/mem_blockdev.o \
|
||||
lib/fs/fs.o \
|
||||
lib/fs/debug.o \
|
||||
lib/fs/hfs/cache.o \
|
||||
lib/fs/hfs/hfs.o \
|
||||
lib/fs/hfs/hfs_fs.o
|
||||
|
||||
TEST_SUPPORT_OBJS := \
|
||||
lib/env/env.o \
|
||||
tests/mocks/blockdev.o \
|
||||
tests/mocks/sys/security.o
|
||||
|
||||
TEST_CFLAGS := \
|
||||
-DCPU_CACHELINE_SIZE=32 \
|
||||
-DHFS_UNITTEST=1
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := fs-fuzz
|
||||
|
||||
include $(LOCAL_DIR)/tests-common.mk
|
||||
|
||||
TEST_SUPPORT_OBJS += \
|
||||
tests/fuzz-main.o \
|
||||
$(LOCAL_DIR)/fuzz.o
|
||||
|
||||
TEST_CFLAGS += \
|
||||
-DNO_MOCK_ASSERTS=1
|
||||
|
||||
TEST_BUILD_ONLY := YES
|
||||
@@ -0,0 +1,180 @@
|
||||
#include <stdint.h>
|
||||
#include <unittest.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <lib/fs.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct blockdev *create_mock_blockdev(const char *name, const char *filename, uint32_t block_size);
|
||||
|
||||
void test_fs(uintptr_t context) {
|
||||
struct blockdev *bdev;
|
||||
int result;
|
||||
int handle;
|
||||
void *buffer;
|
||||
size_t load_size;
|
||||
size_t buf_size;
|
||||
|
||||
const char *filename = getenv("TESTFS_IMAGE");
|
||||
TEST_ASSERT_NOT_NULL(filename);
|
||||
|
||||
bdev = create_mock_blockdev("dev", filename, 4096);
|
||||
register_blockdev(bdev);
|
||||
|
||||
result = fs_mount("dev", "hfs", "/boot");
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
|
||||
fs_dump_dir("/boot", true);
|
||||
fs_dump_dir("/boot/dir", true);
|
||||
|
||||
// Opening a non-existant path should fail
|
||||
handle = fs_opendir("/boot/fake");
|
||||
TEST_ASSERT_LT(handle, 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/a")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/a/a")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/bb")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/bb/bb-a")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/bb/bb-b")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/ccc")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/dddd")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/eeeee")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/ffffff")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/ggggggg")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/hhhhhhhh")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/iiiiiiiii")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/jjjjjjjjjj")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/kkkkkkkkkkk")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/lllllllllllllllllllllllllllllllllllll")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/m")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/n")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/o")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/p")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/q")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
TEST_ASSERT_GTE((handle = fs_opendir("/boot/dir")), 0);
|
||||
TEST_ASSERT_EQ(fs_closedir(handle), 0);
|
||||
|
||||
buf_size = 1024 * 1024;
|
||||
buffer = malloc(buf_size);
|
||||
|
||||
// Loading non-existant file should fail
|
||||
load_size = buf_size;
|
||||
result = fs_load_file("/boot/testfile1", (addr_t)buffer, &load_size);
|
||||
TEST_ASSERT_EQ(result, -1);
|
||||
|
||||
// Load a file with 1 byte of zeroes
|
||||
load_size = buf_size;
|
||||
result = fs_load_file("/boot/dir/testfile1", (addr_t)buffer, &load_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
TEST_ASSERT_EQ(load_size, 1);
|
||||
|
||||
// Load a file with 2 bytes of zeroes
|
||||
load_size = buf_size;
|
||||
result = fs_load_file("/boot/dir/testfile2", (addr_t)buffer, &load_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
TEST_ASSERT_EQ(load_size, 2);
|
||||
|
||||
// Load a file with 512 bytes of zeroes
|
||||
load_size = buf_size;
|
||||
result = fs_load_file("/boot/dir/testfile512", (addr_t)buffer, &load_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
TEST_ASSERT_EQ(load_size, 512);
|
||||
|
||||
// Load a file with 1024 bytes of zeroes
|
||||
load_size = buf_size;
|
||||
result = fs_load_file("/boot/dir/testfile1024", (addr_t)buffer, &load_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
TEST_ASSERT_EQ(load_size, 1024);
|
||||
|
||||
// Load a file with a repeating data pattern
|
||||
const char *pattern = "evYya7KUSI1y5Tc05IQftxDuoIz8SlIRHDQLTA\n";
|
||||
size_t pattern_len = strlen(pattern);
|
||||
load_size = buf_size;
|
||||
result = fs_load_file("/boot/dir/testfile-evYya7KUSI1y5Tc05IQftxDuoIz8SlIRHDQLTA", (addr_t)buffer, &load_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
TEST_ASSERT_EQ(load_size, pattern_len * 1024);
|
||||
char *current = buffer;
|
||||
for (unsigned i = 0; i < 1024; i++) {
|
||||
TEST_ASSERT_MEM_EQ(current, pattern, pattern_len);
|
||||
current += pattern_len;
|
||||
}
|
||||
|
||||
// Verify a non-existant file is not found in a directory that exists
|
||||
load_size = buf_size;
|
||||
result = fs_load_file("/boot/dir/non-existant", (addr_t)buffer, &load_size);
|
||||
TEST_ASSERT_EQ(result, -1);
|
||||
|
||||
// Verify a existant file is not found in the wrong directory
|
||||
load_size = buf_size;
|
||||
result = fs_load_file("/boot/non-existant/testfile1", (addr_t)buffer, &load_size);
|
||||
TEST_ASSERT_EQ(result, -1);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
struct test_suite fs_suite = {
|
||||
.name = "fs",
|
||||
.test_cases = {
|
||||
{ "fs", test_fs, 0, "" },
|
||||
TEST_CASE_LAST
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE(fs_suite);
|
||||
|
||||
// Mock
|
||||
bool
|
||||
env_blacklist(const char *name, bool write)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mock
|
||||
bool
|
||||
env_blacklist_nvram(const char *name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := fs
|
||||
|
||||
include $(LOCAL_DIR)/tests-common.mk
|
||||
|
||||
TEST_SUPPORT_OBJS += \
|
||||
tests/unittest-main.o \
|
||||
$(LOCAL_DIR)/tests.o
|
||||
|
||||
HELPER := test-hfs-helper.sh
|
||||
HELPER_INSTALL := $(call TOTESTDIR,$(HELPER))
|
||||
|
||||
$(HELPER_INSTALL): $(LOCAL_DIR)/$(HELPER)
|
||||
$(_v)cp $< $@
|
||||
|
||||
EXTRA_TEST_PRODUCTS += $(HELPER_INSTALL)
|
||||
|
||||
TEST_RUN_COMMAND := ./$(HELPER)
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LIBFS_DIR := $(GET_LOCAL_DIR)
|
||||
LIBFS_BUILD := $(call TOLIBDIR,$(LIBFS_DIR)/LIBFS.a)
|
||||
COMMONLIBS += LIBFS
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
LIBFS_OBJS := \
|
||||
$(LIBFS_DIR)/fs.o \
|
||||
$(LIBFS_DIR)/debug.o
|
||||
|
||||
LIBFS_OBJS := $(call TOLIBOBJDIR,$(LIBFS_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBFS_OBJS:%o=%d)
|
||||
|
||||
$(LIBFS_BUILD): $(LIBFS_OBJS)
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2008, 2014 Apple Inc. All rights reserved.
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
OPTIONS += WITH_FS=1
|
||||
MODULES += lib/partition
|
||||
LIBRARY_MODULES += lib/fs
|
||||
|
||||
+1296
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2010, 2014 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 <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
extern void* heap_malloc(size_t size, const char *caller_name);
|
||||
extern void* heap_calloc(size_t count, size_t size, const char *caller_name);
|
||||
extern void* heap_realloc(void *ptr, size_t size, const char *caller_name);
|
||||
extern void heap_free(void *ptr);
|
||||
extern void bzero(void *s, size_t n);
|
||||
extern void* memcpy(void* dest, const void* src, size_t len);
|
||||
extern void* memset(void* dest, int val, size_t len);
|
||||
|
||||
/* Global stderrp definition */
|
||||
void* __stderrp = 0;
|
||||
|
||||
|
||||
void* malloc(size_t size)
|
||||
{
|
||||
return heap_malloc(size, NULL);
|
||||
}
|
||||
|
||||
void* realloc(void *ptr, size_t size)
|
||||
{
|
||||
return heap_realloc(ptr, size, NULL);
|
||||
}
|
||||
|
||||
void* calloc(size_t count, size_t size)
|
||||
{
|
||||
return heap_calloc(count, size, NULL);
|
||||
}
|
||||
|
||||
void free(void *ptr)
|
||||
{
|
||||
heap_free(ptr);
|
||||
}
|
||||
|
||||
void* __memcpy_chk(void* dest, const void* src, size_t len, size_t destSize)
|
||||
{
|
||||
memcpy(dest, src, (len > destSize) ? destSize : len);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void* __memset_chk(void* dest, int val, size_t len, size_t destSize)
|
||||
{
|
||||
memset(dest, val, (len > destSize) ? destSize : len);
|
||||
return dest;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2014 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.
|
||||
#
|
||||
|
||||
LIBHEAP_DIR := $(GET_LOCAL_DIR)
|
||||
LIBHEAP_BUILD := $(call TOLIBDIR,$(LIBHEAP_DIR)/LIBHEAP.a)
|
||||
COMMONLIBS += LIBHEAP
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
LIBHEAP_OBJS := $(LIBHEAP_DIR)/heap.o \
|
||||
$(LIBHEAP_DIR)/libc_stub.o
|
||||
|
||||
LIBHEAP_OBJS := $(call TOLIBOBJDIR,$(LIBHEAP_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBHEAP_OBJS:%o=%d)
|
||||
|
||||
$(LIBHEAP_BUILD): $(LIBHEAP_OBJS)
|
||||
|
||||
endif
|
||||
Loaded 100 of 2612 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user