first and last commit

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

No files matched your search

+541
View File
@@ -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;
}
+260
View File
@@ -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;
}
+31
View File
@@ -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
+77
View File
@@ -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;
}
+13
View File
@@ -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
+92
View File
@@ -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;
}
+470
View File
@@ -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);
+27
View File
@@ -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
View File
@@ -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;
}
+29
View File
@@ -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
+13
View File
@@ -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
+81
View File
@@ -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;
}
+341
View File
@@ -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__)
+318
View File
@@ -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
View File
@@ -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;
}
+53
View File
@@ -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;
}
+35
View File
@@ -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;
}
+33
View File
@@ -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
+13
View File
@@ -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
+220
View File
@@ -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;
}
+230
View File
@@ -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);
+39
View File
@@ -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
+682
View File
@@ -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;
}
+67
View File
@@ -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
+150
View File
@@ -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;
}
+32
View File
@@ -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
+105
View File
@@ -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;
}
+22
View File
@@ -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"
+29
View File
@@ -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
+14
View File
@@ -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
+23
View File
@@ -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
+876
View File
@@ -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;
}
+22
View File
@@ -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
+9
View File
@@ -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_ */
+38
View File
@@ -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_ */
+214
View File
@@ -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_ */
+768
View File
@@ -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_ */
+19
View File
@@ -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
+18
View File
@@ -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')
+186
View File
@@ -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);
// =============================================================================
+187
View File
@@ -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;
}
// =============================================================================
+161
View File
@@ -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_ */
+146
View File
@@ -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_ */
+32
View File
@@ -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'.
+221
View File
@@ -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_ */
+30
View File
@@ -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'.
+25
View File
@@ -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 */
+158
View File
@@ -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);
}
// =============================================================================
+54
View File
@@ -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 */
+77
View File
@@ -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>
+537
View File
@@ -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);
}
+38
View File
@@ -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 */
+560
View File
@@ -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
+27
View File
@@ -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
+13
View File
@@ -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
+231
View File
@@ -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);
+20
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+154
View File
@@ -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;
}
+89
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+134
View File
@@ -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_ */
+746
View File
@@ -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__ */
+385
View File
@@ -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));
}
+32
View File
@@ -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
+16
View File
@@ -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
+31
View File
@@ -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
+30
View File
@@ -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
+24
View File
@@ -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
+180
View File
@@ -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;
}
+29
View File
@@ -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)
+29
View File
@@ -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
+15
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+57
View File
@@ -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;
}
+28
View File
@@ -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