first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2014 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <sys.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <debug.h>
|
||||
#include <lib/blockdev.h>
|
||||
#ifndef HOST_TEST
|
||||
#include <lib/mib.h>
|
||||
#endif
|
||||
#include <lib/libiboot.h>
|
||||
#include <limits.h>
|
||||
|
||||
static struct blockdev *bdev_list = NULL;
|
||||
|
||||
#define BLOCKSIZE_MOD(dev, val) ((val) & ((1<<((dev)->block_shift)) - 1))
|
||||
#define BLOCK_FROM_OFF(dev, off) ((off) >> (dev)->block_shift)
|
||||
#define BLOCK_TO_OFF(dev, block) ((block) << (dev)->block_shift)
|
||||
#define ALIGNMENT_MOD(dev, val) ((val) & ((1<<((dev)->alignment_shift)) - 1))
|
||||
|
||||
static size_t get_cpu_cache_line_size(void)
|
||||
{
|
||||
#ifndef HOST_TEST
|
||||
return mib_get_size(kMIBPlatformCacheLineSize);
|
||||
#elif defined CPU_CACHELINE_SIZE
|
||||
return CPU_CACHELINE_SIZE;
|
||||
#else
|
||||
#error "Cache line size is not defined"
|
||||
#endif
|
||||
}
|
||||
|
||||
struct blockdev *lookup_blockdev(const char *name)
|
||||
{
|
||||
struct blockdev *dev;
|
||||
|
||||
for (dev = bdev_list; dev; dev = dev->next) {
|
||||
if (strcmp(dev->name, name) == 0)
|
||||
return dev;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct blockdev *first_blockdev(void)
|
||||
{
|
||||
return bdev_list;
|
||||
}
|
||||
|
||||
struct blockdev *next_blockdev(struct blockdev *dev)
|
||||
{
|
||||
return dev->next;
|
||||
}
|
||||
|
||||
int register_blockdev(struct blockdev *dev)
|
||||
{
|
||||
ASSERT(dev != NULL);
|
||||
ASSERT(dev->block_size > 0);
|
||||
|
||||
dprintf(DEBUG_INFO, "register_blockdev: dev %p '%s'\n", dev, dev->name);
|
||||
|
||||
dev->next = bdev_list;
|
||||
bdev_list = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *get_bounce_buf(struct blockdev *dev)
|
||||
{
|
||||
void *result;
|
||||
uint32_t align = __max(dev->alignment, (uint32_t)get_cpu_cache_line_size());
|
||||
uint32_t size = __max(dev->alignment, dev->block_size);
|
||||
|
||||
posix_memalign(&result, align, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* a reasonable default bytewise read routine in case the block device does not
|
||||
* choose to override the read_hook. deblocks the device by reading from the read_block hook.
|
||||
*/
|
||||
static int blockdev_read_hook(struct blockdev *dev, void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
int err;
|
||||
size_t bytes_read;
|
||||
block_addr block;
|
||||
size_t buf_offset;
|
||||
char *buf = NULL;
|
||||
|
||||
/* trim the read len */
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
if ((uint64_t)offset >= dev->total_len)
|
||||
return 0;
|
||||
if ((offset + len) > dev->total_len)
|
||||
len = dev->total_len - offset;
|
||||
|
||||
buf_offset = 0;
|
||||
bytes_read = 0;
|
||||
block = BLOCK_FROM_OFF(dev, offset);
|
||||
|
||||
// dprintf(DEBUG_INFO, "read: ptr %p, buf_offset %d, bytes_read %zd, block %u, len %d, offset %lld\n", ptr, buf_offset, bytes_read, block, len, offset);
|
||||
|
||||
/* read in partial first block */
|
||||
if (BLOCKSIZE_MOD(dev, offset) != 0 || ALIGNMENT_MOD(dev, (uintptr_t)ptr) != 0) {
|
||||
size_t block_off = BLOCKSIZE_MOD(dev, offset);
|
||||
size_t toread = __min(len, dev->block_size - block_off);
|
||||
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
/* copy the partial data */
|
||||
memcpy(ptr, buf + block_off, toread);
|
||||
|
||||
block++;
|
||||
offset += toread;
|
||||
buf_offset += toread;
|
||||
bytes_read += toread;
|
||||
len -= toread;
|
||||
}
|
||||
|
||||
// dprintf(DEBUG_INFO, "read2: ptr %p, buf_offset %d, bytes_read %zd, block %u, len %d, offset %lld, blockshift %d\n", ptr, buf_offset, bytes_read, block, len, offset, dev->block_shift);
|
||||
|
||||
ASSERT(len == 0 || BLOCKSIZE_MOD(dev, offset) == 0);
|
||||
|
||||
/* handle multiple middle blocks */
|
||||
while (len >= dev->block_size) {
|
||||
size_t blocks_to_read;
|
||||
size_t alignment_offset;
|
||||
|
||||
blocks_to_read = BLOCK_FROM_OFF(dev, len);
|
||||
|
||||
/* If the buffer we're writing to isn't aligned, shorten the read by a block and
|
||||
bounce through an aligned address higher up in the buffer. This relies on the block
|
||||
size being >= the alignment, which is enforced by blockdev_set_buffer_alignment.
|
||||
For reads of just one block, go through the bounce buffer */
|
||||
alignment_offset = ALIGNMENT_MOD(dev, (uintptr_t)ptr + buf_offset);
|
||||
if (alignment_offset != 0) {
|
||||
if (blocks_to_read > 1) {
|
||||
uint8_t *dest;
|
||||
dest = (uint8_t *)ptr + buf_offset + dev->alignment - alignment_offset;
|
||||
|
||||
err = blockdev_read_block(dev, dest, block, blocks_to_read - 1);
|
||||
if (err > 0)
|
||||
memmove((uint8_t *)ptr + buf_offset, dest, BLOCK_TO_OFF(dev, err));
|
||||
} else {
|
||||
if (!buf)
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err > 0)
|
||||
memmove((uint8_t *)ptr + buf_offset, buf, BLOCK_TO_OFF(dev, 1));
|
||||
}
|
||||
} else {
|
||||
err = blockdev_read_block(dev, (uint8_t *)ptr + buf_offset, block, blocks_to_read);
|
||||
}
|
||||
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
/* account for blocks read */
|
||||
block += err;
|
||||
buf_offset += BLOCK_TO_OFF(dev, err);
|
||||
bytes_read += BLOCK_TO_OFF(dev, err);
|
||||
len -= BLOCK_TO_OFF(dev, err);
|
||||
}
|
||||
|
||||
// dprintf(DEBUG_INFO, "read3: ptr %p, buf_offset %d, bytes_read %zd, block %ud, len %d, offset %lld\n", ptr, buf_offset, bytes_read, block, len, offset);
|
||||
|
||||
/* handle partial last block */
|
||||
if (len > 0) {
|
||||
ASSERT(len < dev->block_size);
|
||||
|
||||
if (!buf)
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
memcpy((uint8_t *)ptr + buf_offset, buf, len);
|
||||
|
||||
bytes_read += len;
|
||||
}
|
||||
|
||||
done:
|
||||
if (NULL != buf)
|
||||
free(buf);
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
|
||||
static int blockdev_read_block_hook(struct blockdev *dev, void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "no reasonable default block read routine\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int blockdev_write_hook(struct blockdev *dev, const void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
int err;
|
||||
size_t bytes_written;
|
||||
block_addr block;
|
||||
size_t buf_offset;
|
||||
char *buf = NULL;
|
||||
const void *src;
|
||||
|
||||
/* trim the write len */
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
if ((uint64_t)offset >= dev->total_len)
|
||||
return 0;
|
||||
if ((offset + len) > dev->total_len)
|
||||
len = dev->total_len - offset;
|
||||
|
||||
buf_offset = 0;
|
||||
bytes_written = 0;
|
||||
block = BLOCK_FROM_OFF(dev, offset);
|
||||
|
||||
// dprintf(DEBUG_INFO, "write: ptr %p, buf_offset %d, bytes_written %zd, block %u, len %d, offset %lld\n", ptr, buf_offset, bytes_written, block, len, offset);
|
||||
|
||||
/* write partial first block */
|
||||
if (BLOCKSIZE_MOD(dev, offset) != 0 || ALIGNMENT_MOD(dev, offset) != 0) {
|
||||
size_t block_off = BLOCKSIZE_MOD(dev, offset);
|
||||
size_t towrite = __min(len, dev->block_size - block_off);
|
||||
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
/* read in a buffer */
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
/* copy the partial data */
|
||||
memcpy(buf + block_off, ptr, towrite);
|
||||
|
||||
/* write it back out */
|
||||
err = blockdev_write_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
block++;
|
||||
offset += towrite;
|
||||
buf_offset += towrite;
|
||||
bytes_written += towrite;
|
||||
len -= towrite;
|
||||
}
|
||||
|
||||
// dprintf(DEBUG_INFO, "write2: ptr %p, buf_offset %d, bytes_written %zd, block %u, len %d, offset %lld, blockshift %d\n", ptr, buf_offset, bytes_written, block, len, offset, dev->block_shift);
|
||||
|
||||
ASSERT(len == 0 || BLOCKSIZE_MOD(dev, offset) == 0);
|
||||
|
||||
/* handle multiple middle blocks */
|
||||
while (len >= dev->block_size) {
|
||||
size_t blocks_to_write;
|
||||
size_t alignment_offset;
|
||||
|
||||
/* If the buffer we're reading from isn't aligned, bounce buffer each block. iBoot
|
||||
doesn't care about write performance for big writes, so it's ok for this to be slow */
|
||||
alignment_offset = ALIGNMENT_MOD(dev, (uintptr_t)ptr + buf_offset);
|
||||
if (alignment_offset != 0) {
|
||||
if (!buf)
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
blocks_to_write = 1;
|
||||
memcpy(buf, (const uint8_t *)ptr + buf_offset, BLOCK_TO_OFF(dev, blocks_to_write));
|
||||
src = buf;
|
||||
} else {
|
||||
blocks_to_write = BLOCK_FROM_OFF(dev, len);
|
||||
src = (const uint8_t *)ptr + buf_offset;
|
||||
}
|
||||
|
||||
err = blockdev_write_block(dev, src, block, blocks_to_write);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
/* account for write */
|
||||
block += err;
|
||||
buf_offset += BLOCK_TO_OFF(dev, err);
|
||||
bytes_written += BLOCK_TO_OFF(dev, err);
|
||||
len -= BLOCK_TO_OFF(dev, err);
|
||||
}
|
||||
|
||||
// dprintf(DEBUG_INFO, "write3: ptr %p, buf_offset %d, bytes_written %zd, block %ud, len %d, offset %lld\n", ptr, buf_offset, bytes_written, block, len, offset);
|
||||
|
||||
/* handle partial last block */
|
||||
if (len > 0) {
|
||||
ASSERT(len < dev->block_size);
|
||||
|
||||
if (!buf)
|
||||
buf = get_bounce_buf(dev);
|
||||
|
||||
err = blockdev_read_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
memcpy(buf, (uint8_t *)ptr + buf_offset, len);
|
||||
|
||||
err = blockdev_write_block(dev, buf, block, 1);
|
||||
if (err <= 0)
|
||||
goto done;
|
||||
|
||||
bytes_written += len;
|
||||
}
|
||||
|
||||
done:
|
||||
if (NULL != buf)
|
||||
free(buf);
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
static int blockdev_write_block_hook(struct blockdev *dev, const void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "no reasonable default block write routine\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int blockdev_erase_hook(struct blockdev *dev, off_t offset, uint64_t len)
|
||||
{
|
||||
dprintf(DEBUG_CRITICAL, "no reasonable default erase routine\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* compare memory with a range of the block device */
|
||||
int blockdev_compare(struct blockdev *dev, const void *_ptr, off_t bdev_offset, uint64_t len)
|
||||
{
|
||||
int result;
|
||||
int err;
|
||||
uint32_t i;
|
||||
uint8_t *buf;
|
||||
const uint8_t *ptr = (const uint8_t *)_ptr;
|
||||
|
||||
posix_memalign((void **)&buf, get_cpu_cache_line_size(), dev->block_size);
|
||||
|
||||
result = 0;
|
||||
while (len > 0) {
|
||||
size_t toread = __min(dev->block_size, len);
|
||||
|
||||
err = blockdev_read(dev, buf, bdev_offset, toread);
|
||||
if (err <= 0) {
|
||||
dprintf(DEBUG_CRITICAL, "bdev_compare: error reading from device at offset %lld\n", bdev_offset);
|
||||
result = -1;
|
||||
goto out;
|
||||
}
|
||||
if ((uint32_t)err < toread) {
|
||||
dprintf(DEBUG_CRITICAL, "bdev_compare: short read from device at offset %lld (read %d)\n", bdev_offset, err);
|
||||
result = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i=0; i < toread; i++) {
|
||||
if (buf[i] != *ptr) {
|
||||
dprintf(DEBUG_CRITICAL, "difference at offset %lld: dev 0x%02x mem 0x%02x\n", bdev_offset + i, buf[i], *ptr);
|
||||
result++;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
len -= toread;
|
||||
bdev_offset += toread;
|
||||
}
|
||||
|
||||
out:
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* write to the bdev safely, avoiding the protected region */
|
||||
int blockdev_write_protected(struct blockdev *dev, const void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
size_t overlap;
|
||||
off_t protect_start, protect_end;
|
||||
uint64_t total_length;
|
||||
int err;
|
||||
off_t bytes_written = 0;
|
||||
|
||||
// printf("write_protected: ptr %p offset 0x%llx length 0x%x\n", ptr, offset, len);
|
||||
protect_start = dev->protect_start;
|
||||
protect_end = dev->protect_end;
|
||||
total_length = (uint64_t)offset + len;
|
||||
// Test for negative offset and overlap
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
RELEASE_ASSERT(total_length >= len);
|
||||
|
||||
/* write the portion before the protected region */
|
||||
if (offset < protect_start) {
|
||||
ASSERT(protect_start >= 0);
|
||||
if ((offset + len) > (size_t)protect_start) {
|
||||
overlap = (offset + len) - protect_start;
|
||||
} else {
|
||||
overlap = 0;
|
||||
}
|
||||
// printf("BDEV: writing first part: 0x%llx/0x%x\n", offset, len - overlap);
|
||||
err = dev->write_hook(dev, ptr, offset, len - overlap);
|
||||
if (err < 0)
|
||||
return err;
|
||||
bytes_written += err;
|
||||
}
|
||||
|
||||
/* and write the portion after */
|
||||
RELEASE_ASSERT(protect_end >= 0);
|
||||
if ((offset + len) > (size_t)protect_end) {
|
||||
if (offset < protect_end) {
|
||||
overlap = protect_end - offset;
|
||||
} else {
|
||||
overlap = 0;
|
||||
}
|
||||
// printf("BDEV: writing second part: 0x%llx/0x%x\n", offset + overlap, len - overlap);
|
||||
err = dev->write_hook(dev, (char *)ptr + overlap, offset + overlap, len - overlap);
|
||||
if (err < 0)
|
||||
return err;
|
||||
bytes_written += err;
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
|
||||
int blockdev_write_block_protected(struct blockdev *dev, const void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
size_t overlap;
|
||||
size_t protect_start, protect_end;
|
||||
int err;
|
||||
uint32_t total_count;
|
||||
|
||||
// printf("write_block_protected: ptr %p block 0x%x count 0x%x\n", ptr, block, count);
|
||||
protect_start = dev->protect_start >> dev->block_shift;
|
||||
protect_end = dev->protect_end >> dev->block_shift;
|
||||
total_count = block + count;
|
||||
// Test for overlap
|
||||
RELEASE_ASSERT(total_count >= block);
|
||||
|
||||
/* write the portion before the protected region */
|
||||
if (block < protect_start) {
|
||||
if ((block + count) > protect_start) {
|
||||
overlap = (block + count) - protect_start;
|
||||
} else {
|
||||
overlap = 0;
|
||||
}
|
||||
// printf("BDEV: writing first part: 0x%x/0x%x\n", block, count - overlap);
|
||||
err = dev->write_block_hook(dev, ptr, block, count - overlap);
|
||||
if (err < 0) /* error */
|
||||
return(-1);
|
||||
if ((uint32_t)err < (count - overlap)) /* short write */
|
||||
return(err);
|
||||
|
||||
}
|
||||
|
||||
/* and write the portion after */
|
||||
if ((block + count) > protect_end) {
|
||||
if (block < protect_end) {
|
||||
overlap = protect_end - block;
|
||||
} else {
|
||||
overlap = 0;
|
||||
}
|
||||
// printf("BDEV: writing second part: 0x%x/0x%x\n", block + overlap, count - overlap);
|
||||
err = dev->write_block_hook(dev, (char *)ptr + (overlap << dev->block_shift), block + overlap, count - overlap);
|
||||
if (err < 0) /* error */
|
||||
return(-1);
|
||||
if ((uint32_t)err < (count - overlap)) /* short write XXX does not handle both ends
|
||||
* overhanging */
|
||||
return(err);
|
||||
}
|
||||
return(count);
|
||||
}
|
||||
|
||||
/* set the protected region */
|
||||
int blockdev_set_protection(struct blockdev *dev, off_t offset, uint64_t length)
|
||||
{
|
||||
if (dev->protect_end == dev->protect_start) {
|
||||
dev->protect_start = offset;
|
||||
dev->protect_end = offset + length;
|
||||
dprintf(DEBUG_INFO, "BDEV: protecting 0x%llx-0x%llx\n", dev->protect_start, dev->protect_end);
|
||||
return(0);
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* set the required buffer alignment for read and write buffers */
|
||||
void blockdev_set_buffer_alignment(struct blockdev *dev, uint32_t alignment)
|
||||
{
|
||||
ASSERT(dev != NULL);
|
||||
|
||||
ASSERT(is_pow2(alignment));
|
||||
|
||||
/* Standard hooks don't support an alignment requirement bigger than the block size.
|
||||
Drivers that need this can just make their blocksize equal to the alignment and
|
||||
break the alignment-sized blocks into their intrinsic blocksize in their read block hook */
|
||||
ASSERT(alignment <= dev->block_size);
|
||||
|
||||
/* can't change the alignment after first setting it */
|
||||
ASSERT(dev->alignment == 1 && dev->alignment_shift == 0);
|
||||
|
||||
dev->alignment = alignment;
|
||||
dev->alignment_shift = log2_int(dev->alignment);
|
||||
}
|
||||
|
||||
/*
|
||||
* set up the shared part of the block device structure, and set the default read hooks
|
||||
* that provide a lot of the base functionality. The specific block device implementation should
|
||||
* customize the structure further
|
||||
*/
|
||||
int construct_blockdev(struct blockdev *dev, const char *name, uint64_t len, uint32_t block_size)
|
||||
{
|
||||
ASSERT(dev != NULL);
|
||||
ASSERT(is_pow2(block_size));
|
||||
ASSERT(strlen(name) < sizeof(dev->name));
|
||||
|
||||
strlcpy(dev->name, name, sizeof(dev->name));
|
||||
dev->flags = 0;
|
||||
dev->block_size = block_size;
|
||||
dev->block_shift = log2_int(dev->block_size);
|
||||
dev->block_count = len >> dev->block_shift;
|
||||
dev->total_len = len;
|
||||
dev->alignment = 1;
|
||||
dev->alignment_shift = 0;
|
||||
|
||||
ASSERT((len >> dev->block_shift) < UINT_MAX);
|
||||
|
||||
dev->read_hook = &blockdev_read_hook;
|
||||
dev->read_block_hook = &blockdev_read_block_hook;
|
||||
dev->write_hook = &blockdev_write_hook;
|
||||
dev->write_block_hook = &blockdev_write_block_hook;
|
||||
dev->erase_hook = &blockdev_erase_hook;
|
||||
|
||||
dev->protect_start = 0;
|
||||
dev->protect_end = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2011, 2013-2016 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <lib/env.h>
|
||||
#include <lib/mib.h>
|
||||
#include <sys/menu.h>
|
||||
#include <drivers/usb/usb_debug.h>
|
||||
|
||||
#define USB_TRANSFER_BLOCKS (1000)
|
||||
|
||||
static int do_slurp (struct blockdev *dev, char *filename, const size_t block_offset) {
|
||||
int err = 0;
|
||||
off_t lba;
|
||||
off_t end;
|
||||
size_t buf_offset;
|
||||
const size_t buf_size = dev->block_size * USB_TRANSFER_BLOCKS;
|
||||
uint8_t * read_buf;
|
||||
uint8_t * base_buf;
|
||||
uintptr_t buf_physical;
|
||||
|
||||
|
||||
end = dev->total_len >> dev->block_shift;
|
||||
base_buf = malloc(buf_size);
|
||||
if (NULL == base_buf) {
|
||||
printf ("malloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
buf_physical = mem_static_map_physical((uintptr_t)base_buf);
|
||||
|
||||
printf("slurping lba %zu to %llu\n", block_offset, end);
|
||||
|
||||
buf_offset = 0;
|
||||
lba = block_offset;
|
||||
|
||||
while (lba < end) {
|
||||
read_buf = base_buf + buf_offset;
|
||||
*(UInt32*)read_buf = 0xa5a5a5a5; /* poison buffer */
|
||||
err = blockdev_read(dev, read_buf, lba << dev->block_shift, dev->block_size);
|
||||
if (err < 0){
|
||||
printf("blockdev_read returns %d on lba %llu\n\n", err, lba);
|
||||
// don't stop slurping
|
||||
}
|
||||
buf_offset += dev->block_size;
|
||||
++lba;
|
||||
if (buf_offset == buf_size) {
|
||||
/*
|
||||
* This issues writes over the USB pipe for USB_TRANSFER_BLOCKS ' worth of
|
||||
* device blocks. This could be (4096) * 1000 or (8192) * 1000 depending
|
||||
* on device block size.
|
||||
*/
|
||||
err = usb_send_data_to_file(filename, buf_offset, buf_physical, 0);
|
||||
if (err) {
|
||||
printf("USB transfer error %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
buf_offset = 0;
|
||||
printf("Completed to block offset %llu\r", lba);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send over the remainder of blocks.. */
|
||||
if (buf_offset > 0) {
|
||||
err = usb_send_data_to_file(filename, buf_offset, buf_physical, 0);
|
||||
if (err) {
|
||||
printf("USB transfer error %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
printf("Completed to block offset %llu with code %d\n", lba, err);
|
||||
|
||||
free(base_buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int do_blockdev(int argc, struct cmd_arg *args)
|
||||
{
|
||||
if (!security_allow_modes(kSecurityModeDebugCmd)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
printf("not enough arguments\n");
|
||||
usage:
|
||||
printf("usage:\n");
|
||||
printf("%s list\n", args[0].str);
|
||||
printf("%s read <dev> <offset> <len> [<address>]\n", args[0].str);
|
||||
printf("%s scan <dev> <lba> <num>\n", args[0].str);
|
||||
printf("%s slurp <dev> <file> \n", args[0].str);
|
||||
printf("%s write <dev> <offset> [<len>] [<address>]\n", args[0].str);
|
||||
printf("%s memcmp <dev> <offset> [<len>] [<address>]\n", args[0].str);
|
||||
printf("%s erase <dev> <offset> <len>\n", args[0].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(args[1].str, "list")) {
|
||||
struct blockdev *dev;
|
||||
|
||||
printf("block device list:\n");
|
||||
for (dev = first_blockdev(); dev; dev = next_blockdev(dev)) {
|
||||
printf("\tdevice '%s': size %lld, block_size %d\n", dev->name, dev->total_len, dev->block_size);
|
||||
}
|
||||
} else if (!strcmp(args[1].str, "read") || !strcmp(args[1].str, "write") || !strcmp(args[1].str, "memcmp")) {
|
||||
if (argc < 4) {
|
||||
printf("not enough arguments.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
off_t offset = args[3].u;
|
||||
size_t len;
|
||||
addr_t addr;
|
||||
int err;
|
||||
struct blockdev *dev;
|
||||
|
||||
if (argc >= 5) {
|
||||
len = args[4].u;
|
||||
} else {
|
||||
len = env_get_uint("filesize", 0);
|
||||
if (len == 0) {
|
||||
printf("filesize variable invalid or not set, aborting\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc >= 6) {
|
||||
addr = args[5].u;
|
||||
} else {
|
||||
addr = env_get_uint("loadaddr", mib_get_addr(kMIBTargetDefaultLoadAddress));
|
||||
}
|
||||
|
||||
if (!security_allow_memory((void *)addr, len)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev = lookup_blockdev(args[2].str);
|
||||
if (!dev) {
|
||||
printf("couldn't find block device '%s'\n", args[2].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(args[1].str, "read")) {
|
||||
printf("reading %zu bytes from device '%s' to address 0x%llx\n", len, args[2].str, (uint64_t)addr);
|
||||
err = blockdev_read(dev, (void *)addr, offset, len);
|
||||
printf("blockdev_read returns %d\n", err);
|
||||
} else if (!strcmp(args[1].str, "write")) {
|
||||
printf("writing %zu bytes to device '%s' from address 0x%llx\n", len, args[2].str, (uint64_t)addr);
|
||||
err = blockdev_write(dev, (void *)addr, offset, len);
|
||||
printf("blockdev_write returns %d\n", err);
|
||||
} else {
|
||||
printf("comparing %zu bytes from device '%s' to address 0x%llx\n", len, args[2].str, (uint64_t)addr);
|
||||
err = blockdev_compare(dev, (void *)addr, offset, len);
|
||||
printf("blockdev_compare returns %d\n", err);
|
||||
}
|
||||
} else if (!strcmp(args[1].str, "erase")) {
|
||||
if (argc < 5) {
|
||||
printf("not enough arguments.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
off_t offset = args[3].u;
|
||||
size_t len = args[4].u;
|
||||
int err;
|
||||
struct blockdev *dev;
|
||||
|
||||
dev = lookup_blockdev(args[2].str);
|
||||
if (!dev) {
|
||||
printf("couldn't find block device '%s'\n", args[2].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = blockdev_erase(dev, offset, len);
|
||||
printf("blockdev_erase returns %d\n", err);
|
||||
} else if (!strcmp(args[1].str, "scan")) {
|
||||
if (argc < 5) {
|
||||
printf("not enough arguments.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
off_t lba = (off_t)args[3].u;
|
||||
size_t len = args[4].u;
|
||||
int err;
|
||||
struct blockdev *dev;
|
||||
off_t end;
|
||||
uint8_t *buffer;
|
||||
|
||||
dev = lookup_blockdev(args[2].str);
|
||||
if (!dev) {
|
||||
printf("couldn't find block device '%s'\n", args[2].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!len || ((off_t)(lba + len) > (off_t)(dev->total_len >> dev->block_shift))){
|
||||
end = dev->total_len >> dev->block_shift;
|
||||
}else{
|
||||
end = lba + len;
|
||||
}
|
||||
|
||||
buffer = malloc(dev->block_size);
|
||||
|
||||
printf("scanning lba %llu to %llu\n", lba, end);
|
||||
while (lba < end)
|
||||
{
|
||||
*buffer = 0xa5;
|
||||
err = blockdev_read(dev, buffer, lba << dev->block_shift, dev->block_size);
|
||||
if (err < 0){
|
||||
printf("blockdev_read returns %d on lba %llu\n", err, lba);
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
if (lba % 1000 == 0)
|
||||
printf("lba %zu\r", (size_t)lba);
|
||||
++lba;
|
||||
}
|
||||
free(buffer);
|
||||
printf("blockdev scan completed with no errors\n");
|
||||
}
|
||||
else if (!strcmp(args[1].str, "slurp")) {
|
||||
/* Is it a slurp? */
|
||||
if (argc < 3) {
|
||||
/* bdev slurp device filename */
|
||||
printf("slurp invalid arguments \n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
struct blockdev *dev;
|
||||
size_t block_offset;
|
||||
|
||||
dev = lookup_blockdev(args[2].str);
|
||||
block_offset = 0;
|
||||
|
||||
if (!dev) {
|
||||
printf("could not find blk dev ' %s ' \n", args[2].str);
|
||||
goto usage;
|
||||
}
|
||||
|
||||
return do_slurp (dev, args[3].str, block_offset);
|
||||
|
||||
}
|
||||
else {
|
||||
printf("unrecognized subcommand.\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
LIBBLOCKDEV_DIR := $(GET_LOCAL_DIR)
|
||||
LIBBLOCKDEV_BUILD := $(call TOLIBDIR,$(LIBBLOCKDEV_DIR)/LIBBLOCKDEV.a)
|
||||
COMMONLIBS += LIBBLOCKDEV
|
||||
|
||||
# Only process the remainder of the makefile if we are building libraries
|
||||
#
|
||||
ifeq ($(MAKEPHASE),libraries)
|
||||
|
||||
LIBBLOCKDEV_OBJS := \
|
||||
$(LIBBLOCKDEV_DIR)/blockdev.o \
|
||||
$(LIBBLOCKDEV_DIR)/subdev.o \
|
||||
$(LIBBLOCKDEV_DIR)/mem_blockdev.o \
|
||||
$(LIBBLOCKDEV_DIR)/debug.o
|
||||
|
||||
LIBBLOCKDEV_OBJS := $(call TOLIBOBJDIR,$(LIBBLOCKDEV_OBJS))
|
||||
|
||||
ALL_DEPS += $(LIBBLOCKDEV_OBJS:%o=%d)
|
||||
|
||||
$(LIBBLOCKDEV_BUILD): $(LIBBLOCKDEV_OBJS)
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2011 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <sys.h>
|
||||
#include <string.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct mem_blockdev {
|
||||
struct blockdev bdev;
|
||||
|
||||
/* local data */
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
static int mem_blockdev_read_block(struct blockdev *bdev, void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct mem_blockdev *mdev = (struct mem_blockdev *)bdev;
|
||||
|
||||
// no overflow
|
||||
RELEASE_ASSERT(block + count >= block);
|
||||
|
||||
if (block >= mdev->bdev.block_count)
|
||||
return 0;
|
||||
if ((block + count) > mdev->bdev.block_count)
|
||||
count = mdev->bdev.block_count - block;
|
||||
|
||||
dprintf(DEBUG_SPEW, "mem_blockdev_read: dev %p, ptr %p, block %d, count %d\n", bdev, ptr, block, count);
|
||||
|
||||
memcpy(ptr, (uint8_t *)mdev->ptr + (block << mdev->bdev.block_shift), count << mdev->bdev.block_shift);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int mem_blockdev_read(struct blockdev *bdev, void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
struct mem_blockdev *mdev = (struct mem_blockdev *)bdev;
|
||||
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
// no overflow
|
||||
RELEASE_ASSERT((uint64_t)offset + len >= (uint64_t)offset);
|
||||
|
||||
if ((uint64_t)offset >= mdev->bdev.total_len)
|
||||
return 0;
|
||||
if ((offset + len) > mdev->bdev.total_len)
|
||||
len = mdev->bdev.total_len - offset;
|
||||
|
||||
dprintf(DEBUG_SPEW, "mem_blockdev_read: dev %p, ptr %p, off %lld, len %llu\n", bdev, ptr, offset, len);
|
||||
|
||||
memcpy(ptr, (uint8_t *)mdev->ptr + offset, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
struct blockdev *create_mem_blockdev(const char *name, void *ptr, uint64_t len, uint32_t block_size)
|
||||
{
|
||||
struct mem_blockdev *mdev;
|
||||
|
||||
mdev = malloc(sizeof(struct mem_blockdev));
|
||||
|
||||
construct_blockdev(&mdev->bdev, name, len, block_size);
|
||||
mdev->ptr = ptr;
|
||||
|
||||
mdev->bdev.read_hook = &mem_blockdev_read;
|
||||
mdev->bdev.read_block_hook = &mem_blockdev_read_block;
|
||||
|
||||
return &mdev->bdev;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2007, 2009-2014 Apple Inc. All rights reserved.
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple, Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
OPTIONS += WITH_BLOCKDEV=1
|
||||
LIBRARY_MODULES += lib/blockdev
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2011 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct subdev_blockdev {
|
||||
struct blockdev bdev;
|
||||
|
||||
/* local data */
|
||||
struct blockdev *parent_dev;
|
||||
off_t offset;
|
||||
};
|
||||
|
||||
static int subdev_blockdev_read(struct blockdev *bdev, void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
struct subdev_blockdev *sdev = (struct subdev_blockdev *)bdev;
|
||||
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
if ((uint64_t)offset >= sdev->bdev.total_len)
|
||||
return 0;
|
||||
if ((offset + len) > sdev->bdev.total_len)
|
||||
len = sdev->bdev.total_len - offset;
|
||||
|
||||
return blockdev_read(sdev->parent_dev, ptr, offset + sdev->offset, len);
|
||||
}
|
||||
|
||||
static int subdev_blockdev_read_block(struct blockdev *bdev, void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct subdev_blockdev *sdev = (struct subdev_blockdev *)bdev;
|
||||
|
||||
/* since our blocks might not be aligned on parent block boundaries, just punch through to the
|
||||
* arbitrary alignment read handler and let it deal with it
|
||||
*/
|
||||
return subdev_blockdev_read(bdev, ptr, block << sdev->bdev.block_shift, count << sdev->bdev.block_shift);
|
||||
}
|
||||
|
||||
static int subdev_blockdev_write(struct blockdev *bdev, const void *ptr, off_t offset, uint64_t len)
|
||||
{
|
||||
struct subdev_blockdev *sdev = (struct subdev_blockdev *)bdev;
|
||||
|
||||
RELEASE_ASSERT(offset >= 0);
|
||||
if ((uint64_t)offset >= sdev->bdev.total_len)
|
||||
return 0;
|
||||
if ((offset + len) > sdev->bdev.total_len)
|
||||
len = sdev->bdev.total_len - offset;
|
||||
|
||||
return blockdev_write(sdev->parent_dev, ptr, offset + sdev->offset, len);
|
||||
}
|
||||
|
||||
static int subdev_blockdev_write_block(struct blockdev *bdev, const void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct subdev_blockdev *sdev = (struct subdev_blockdev *)bdev;
|
||||
|
||||
/* since our blocks might not be aligned on parent block boundaries, just punch through to the
|
||||
* arbitrary alignment write handler and let it deal with it
|
||||
*/
|
||||
return subdev_blockdev_write(bdev, ptr, block << sdev->bdev.block_shift, count << sdev->bdev.block_shift);
|
||||
}
|
||||
|
||||
struct blockdev *create_subdev_blockdev(const char *name, struct blockdev *parent, off_t off, uint64_t len, uint32_t block_size)
|
||||
{
|
||||
struct subdev_blockdev *dev;
|
||||
|
||||
ASSERT(parent);
|
||||
|
||||
dprintf(DEBUG_SPEW, "create_subdev: name '%s' parent '%s' off 0x%llx len 0x%llx bsize 0x%x\n",
|
||||
name, parent->name, off, len, block_size);
|
||||
|
||||
dev = malloc(sizeof(struct subdev_blockdev));
|
||||
|
||||
construct_blockdev(&dev->bdev, name, len, block_size);
|
||||
blockdev_set_buffer_alignment(&dev->bdev, parent->alignment);
|
||||
dev->parent_dev = parent;
|
||||
dev->offset = off;
|
||||
|
||||
dev->bdev.read_hook = &subdev_blockdev_read;
|
||||
dev->bdev.read_block_hook = &subdev_blockdev_read_block;
|
||||
dev->bdev.write_hook = &subdev_blockdev_write;
|
||||
dev->bdev.write_block_hook = &subdev_blockdev_write_block;
|
||||
|
||||
return (struct blockdev *)dev;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
#include <unittest.h>
|
||||
#include <lib/blockdev.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct mock_blockdev {
|
||||
struct blockdev bdev;
|
||||
|
||||
/* local data */
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
static int mock_blockdev_read_block(struct blockdev *bdev, void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct mock_blockdev *mdev = (struct mock_blockdev *)bdev;
|
||||
|
||||
// no overflow
|
||||
TEST_ASSERT_GTE(block + count, block);
|
||||
|
||||
// buffer must be properly aligned
|
||||
TEST_ASSERT_EQ((uintptr_t)ptr & (bdev->alignment - 1), 0);
|
||||
|
||||
if (block >= mdev->bdev.block_count)
|
||||
return 0;
|
||||
if ((block + count) > mdev->bdev.block_count)
|
||||
count = mdev->bdev.block_count - block;
|
||||
|
||||
memcpy(ptr, (uint8_t *)mdev->ptr + (block << mdev->bdev.block_shift), count << mdev->bdev.block_shift);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int mock_blockdev_write_block(struct blockdev *bdev, const void *ptr, block_addr block, uint32_t count)
|
||||
{
|
||||
struct mock_blockdev *mdev = (struct mock_blockdev *)bdev;
|
||||
|
||||
// no overflow
|
||||
TEST_ASSERT_GTE(block + count, block);
|
||||
|
||||
// buffer must be properly aligned
|
||||
TEST_ASSERT_EQ((uintptr_t)ptr & (bdev->alignment - 1), 0);
|
||||
|
||||
if (block >= mdev->bdev.block_count)
|
||||
return 0;
|
||||
if ((block + count) > mdev->bdev.block_count)
|
||||
count = mdev->bdev.block_count - block;
|
||||
|
||||
memcpy(mdev->ptr + (block << mdev->bdev.block_shift), ptr, count << mdev->bdev.block_shift);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
struct blockdev *create_mock_blockdev(const char *name, void *ptr, uint64_t len, uint32_t block_size)
|
||||
{
|
||||
struct mock_blockdev *mdev;
|
||||
|
||||
mdev = malloc(sizeof(struct mock_blockdev));
|
||||
|
||||
construct_blockdev(&mdev->bdev, name, len, block_size);
|
||||
mdev->ptr = ptr;
|
||||
|
||||
mdev->bdev.read_block_hook = &mock_blockdev_read_block;
|
||||
mdev->bdev.write_block_hook = &mock_blockdev_write_block;
|
||||
|
||||
return &mdev->bdev;
|
||||
}
|
||||
|
||||
static void do_blockdev_read_tests(struct blockdev *bdev, uint8_t *backing, size_t backing_size, uint32_t block_size)
|
||||
{
|
||||
uint8_t *readback;
|
||||
uint8_t *zeroes;
|
||||
int result;
|
||||
|
||||
readback = malloc(backing_size + 1);
|
||||
TEST_ASSERT_NOT_NULL(readback);
|
||||
zeroes = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(zeroes);
|
||||
|
||||
memset(zeroes, 0, backing_size);
|
||||
|
||||
// Reading entire buffer should work
|
||||
memset(readback, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback, 0, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size);
|
||||
TEST_ASSERT_MEM_EQ(backing, readback, backing_size);
|
||||
|
||||
// Reading entire buffer unaligned should work
|
||||
memset(readback + 1, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback + 1, 0, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size);
|
||||
TEST_ASSERT_MEM_EQ(backing, readback + 1, backing_size);
|
||||
|
||||
// Reading past the end results in a short read
|
||||
memset(readback, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback, backing_size / 2, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size / 2);
|
||||
// first half of buffer should have result of read
|
||||
TEST_ASSERT_MEM_EQ(readback, backing + backing_size / 2, backing_size / 2);
|
||||
// second half should be untouched
|
||||
TEST_ASSERT_MEM_EQ(readback + backing_size / 2, zeroes, backing_size / 2);
|
||||
|
||||
// Reading totally past the end results in no read
|
||||
memset(readback, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback, backing_size + 2, backing_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
// buffer should be totally untouched
|
||||
TEST_ASSERT_MEM_EQ(readback, zeroes, backing_size);
|
||||
|
||||
// Reads that are weird size should work
|
||||
uint32_t read_sizes[] = {1, 2, 3, 13, 127, 129, 1023, 1025, 4094, 4097, block_size, block_size - 1, block_size - 2, backing_size - 2, backing_size - 1};
|
||||
for (uint32_t i = 0; i < sizeof(read_sizes) / sizeof(read_sizes[0]); i++) {
|
||||
uint32_t read_size = read_sizes[i];
|
||||
if (read_size == 0 || read_size >= backing_size)
|
||||
continue;
|
||||
|
||||
uint32_t read_offsets[] = {0, 1, 2, 3, 13, 127, 129, 1023, 1025, 4094, 4097, block_size, block_size - 1, block_size - 2, backing_size - 2, backing_size - 1};
|
||||
for (uint32_t j = 0; j < sizeof(read_offsets) / sizeof(read_offsets[0]); j++) {
|
||||
uint32_t read_offset = read_offsets[j];
|
||||
if (read_offset >= backing_size || read_offset + read_size >= backing_size)
|
||||
continue;
|
||||
tprintf(TEST_INFO, "doing offset=%u size=%u\n", read_offset, read_size);
|
||||
|
||||
// make sure the read succeeds
|
||||
memset(readback + 1, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback, read_offset, read_size);
|
||||
TEST_ASSERT_EQ(result, read_size);
|
||||
TEST_ASSERT_MEM_EQ(readback, backing + read_offset, read_size);
|
||||
TEST_ASSERT_MEM_EQ(readback + read_size, zeroes, backing_size - read_size);
|
||||
|
||||
// make sure read succeeds with alignment forced off
|
||||
memset(readback + 1, 0, backing_size);
|
||||
result = blockdev_read(bdev, readback + 1, read_offset, read_size);
|
||||
TEST_ASSERT_EQ(result, read_size);
|
||||
TEST_ASSERT_MEM_EQ(readback + 1, backing + read_offset, read_size);
|
||||
TEST_ASSERT_MEM_EQ(readback + 1 + read_size, zeroes, backing_size - read_size);
|
||||
}
|
||||
}
|
||||
|
||||
free(readback);
|
||||
free(zeroes);
|
||||
}
|
||||
|
||||
static void do_blockdev_read_block_tests(struct blockdev *bdev, uint8_t *backing, size_t backing_size, uint32_t block_size)
|
||||
{
|
||||
uint8_t *readback;
|
||||
uint8_t *filler;
|
||||
int result;
|
||||
uint32_t blocks = backing_size / block_size;
|
||||
|
||||
readback = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(readback);
|
||||
filler = malloc(backing_size + 1);
|
||||
TEST_ASSERT_NOT_NULL(filler);
|
||||
|
||||
memset(filler, 0xee, backing_size);
|
||||
|
||||
// Reading entire buffer should work
|
||||
memcpy(readback, filler, backing_size);
|
||||
result = blockdev_read_block(bdev, readback, 0, blocks);
|
||||
TEST_ASSERT_EQ(result, blocks);
|
||||
TEST_ASSERT_MEM_EQ(backing, readback, backing_size);
|
||||
|
||||
// Reading past the end results in a short read
|
||||
memcpy(readback, filler, backing_size);
|
||||
result = blockdev_read_block(bdev, readback, blocks / 2, blocks);
|
||||
TEST_ASSERT_EQ(result, blocks / 2);
|
||||
// first half of buffer should have result of read
|
||||
TEST_ASSERT_MEM_EQ(readback, backing + backing_size / 2, backing_size / 2);
|
||||
// second half should be untouched
|
||||
TEST_ASSERT_MEM_EQ(readback + backing_size / 2, filler, backing_size / 2);
|
||||
|
||||
// Reading totally past the end results in no read
|
||||
memcpy(readback, filler, backing_size);
|
||||
result = blockdev_read_block(bdev, readback, blocks + 2, blocks);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
// buffer should be totally untouched
|
||||
TEST_ASSERT_MEM_EQ(readback, filler, backing_size);
|
||||
|
||||
free(readback);
|
||||
free(filler);
|
||||
}
|
||||
|
||||
void test_mem_blockdev(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(backing);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(backing + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
bdev = create_mem_blockdev("dev", backing, backing_size, block_size);
|
||||
|
||||
do_blockdev_read_tests(bdev, backing, backing_size, block_size);
|
||||
do_blockdev_read_block_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
// make sure backing wasn't touched during all the tests
|
||||
for (unsigned i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val;
|
||||
memcpy(&val, backing + i * sizeof(uint32_t), sizeof(uint32_t));
|
||||
TEST_ASSERT_EQ(val, i + 0xaabbccdd);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
void test_blockdev_read_block(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(backing);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(backing + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
bdev = create_mock_blockdev("dev1", backing, backing_size, block_size);
|
||||
do_blockdev_read_block_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
// make sure backing wasn't touched during all the tests
|
||||
for (unsigned i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val;
|
||||
memcpy(&val, backing + i * sizeof(uint32_t), sizeof(uint32_t));
|
||||
TEST_ASSERT_EQ(val, i + 0xaabbccdd);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
void test_blockdev_read(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(backing);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(backing + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
bdev = create_mock_blockdev("dev1", backing, backing_size, block_size);
|
||||
do_blockdev_read_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
// make sure backing wasn't touched during all the tests
|
||||
for (unsigned i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val;
|
||||
memcpy(&val, backing + i * sizeof(uint32_t), sizeof(uint32_t));
|
||||
TEST_ASSERT_EQ(val, i + 0xaabbccdd);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
void test_blockdev_set_buffer_alignment_read(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(backing);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(backing + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
bdev = create_mock_blockdev("dev1", backing, backing_size, block_size);
|
||||
blockdev_set_buffer_alignment(bdev, block_size);
|
||||
do_blockdev_read_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
if (block_size > 2) {
|
||||
bdev = create_mock_blockdev("dev2", backing, backing_size, block_size);
|
||||
blockdev_set_buffer_alignment(bdev, block_size / 2);
|
||||
do_blockdev_read_tests(bdev, backing, backing_size, block_size);
|
||||
}
|
||||
|
||||
// make sure backing wasn't touched during all the tests
|
||||
for (unsigned i = 0; i < backing_size / sizeof(uint32_t); i++) {
|
||||
uint32_t val;
|
||||
memcpy(&val, backing + i * sizeof(uint32_t), sizeof(uint32_t));
|
||||
TEST_ASSERT_EQ(val, i + 0xaabbccdd);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
static void do_blockdev_write_tests(struct blockdev *bdev, uint8_t *backing, size_t backing_size, uint32_t block_size)
|
||||
{
|
||||
uint8_t *data;
|
||||
uint8_t *filler;
|
||||
int result;
|
||||
|
||||
// need some source data we can write to the blockdev with
|
||||
// easily recognizable patterns
|
||||
data = malloc(backing_size + sizeof(uint32_t));
|
||||
TEST_ASSERT_NOT_NULL(data);
|
||||
for (uint32_t i = 0; i < backing_size / sizeof(uint32_t) + 1; i++) {
|
||||
uint32_t val = i + 0xaabbccdd;
|
||||
memcpy(data + i * sizeof(uint32_t), &val, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
filler = malloc(backing_size);
|
||||
TEST_ASSERT_NOT_NULL(filler);
|
||||
|
||||
memset(filler, 0xee, backing_size);
|
||||
|
||||
// Writing entire buffer should work
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data, 0, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size);
|
||||
TEST_ASSERT_MEM_EQ(backing, data, backing_size);
|
||||
|
||||
// Writing entire buffer unaligned should work
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data + 1, 0, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size);
|
||||
TEST_ASSERT_MEM_EQ(backing, data + 1, backing_size);
|
||||
|
||||
// Writing past the end results in a short write
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data, backing_size / 2, backing_size);
|
||||
TEST_ASSERT_EQ(result, backing_size / 2);
|
||||
// first half of backing should be untouched
|
||||
TEST_ASSERT_MEM_EQ(backing, filler, backing_size / 2);
|
||||
// second half of buffer should have result of write
|
||||
TEST_ASSERT_MEM_EQ(backing + backing_size / 2, data, backing_size / 2);
|
||||
|
||||
// Reading totally past the end results in no write
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data, backing_size + 2, backing_size);
|
||||
TEST_ASSERT_EQ(result, 0);
|
||||
// buffer should be totally untouched
|
||||
TEST_ASSERT_MEM_EQ(backing, filler, backing_size);
|
||||
|
||||
// weird size writes should work
|
||||
uint32_t write_sizes[] = {1, 2, 3, 13, 127, 129, 1023, 1025, 4094, 4097, block_size, block_size - 1, block_size - 2, backing_size - 2, backing_size - 1};
|
||||
for (uint32_t i = 0; i < sizeof(write_sizes) / sizeof(write_sizes[0]); i++) {
|
||||
uint32_t write_size = write_sizes[i];
|
||||
if (write_size == 0 || write_size >= backing_size)
|
||||
continue;
|
||||
|
||||
uint32_t write_offsets[] = {0, 1, 2, 3, 13, 127, 129, 1023, 1025, 4094, 4097, block_size, block_size - 1, block_size - 2, backing_size - 2, backing_size - 1};
|
||||
for (uint32_t j = 0; j < sizeof(write_offsets) / sizeof(write_offsets[0]); j++) {
|
||||
uint32_t write_offset = write_offsets[j];
|
||||
if (write_offset >= backing_size || write_offset + write_size >= backing_size)
|
||||
continue;
|
||||
tprintf(TEST_INFO, "doing offset=%u size=%u\n", write_offset, write_size);
|
||||
|
||||
memcpy(backing, filler, backing_size);
|
||||
result = blockdev_write(bdev, data, write_offset, write_size);
|
||||
TEST_ASSERT_EQ(result, write_size);
|
||||
TEST_ASSERT_MEM_EQ(backing + write_offset, data, write_size);
|
||||
// data before and after write should be untouched
|
||||
TEST_ASSERT_MEM_EQ(backing, filler, write_offset);
|
||||
TEST_ASSERT_MEM_EQ(backing + write_offset + write_size, filler, backing_size - write_size - write_offset);
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
free(filler);
|
||||
}
|
||||
|
||||
void test_blockdev_write(uintptr_t param)
|
||||
{
|
||||
struct blockdev *bdev;
|
||||
uint8_t *backing;
|
||||
size_t backing_size = 8192;
|
||||
uint32_t block_size = param;
|
||||
|
||||
backing = malloc(backing_size);
|
||||
|
||||
bdev = create_mock_blockdev("dev1", backing, backing_size, block_size);
|
||||
do_blockdev_write_tests(bdev, backing, backing_size, block_size);
|
||||
|
||||
if (block_size > 1) {
|
||||
bdev = create_mock_blockdev("dev2", backing, backing_size, block_size);
|
||||
blockdev_set_buffer_alignment(bdev, block_size);
|
||||
do_blockdev_write_tests(bdev, backing, backing_size, block_size);
|
||||
}
|
||||
|
||||
if (block_size > 2) {
|
||||
bdev = create_mock_blockdev("dev3", backing, backing_size, block_size);
|
||||
blockdev_set_buffer_alignment(bdev, block_size / 2);
|
||||
do_blockdev_write_tests(bdev, backing, backing_size, block_size);
|
||||
}
|
||||
|
||||
free(backing);
|
||||
}
|
||||
|
||||
void test_register_blockdev(uintptr_t param)
|
||||
{
|
||||
static uint8_t backing;
|
||||
struct blockdev *bdev0;
|
||||
struct blockdev *bdev1;
|
||||
uint32_t found = 0;
|
||||
|
||||
bdev0 = create_mock_blockdev("lookup-bdev0", &backing, 1, 1);
|
||||
TEST_ASSERT_NOT_NULL(bdev0);
|
||||
bdev1 = create_mock_blockdev("lookup-bdev1", &backing, 1, 1);
|
||||
TEST_ASSERT_NOT_NULL(bdev1);
|
||||
|
||||
TEST_ASSERT_NULL(lookup_blockdev("lookup-bdev0"));
|
||||
TEST_ASSERT_NULL(lookup_blockdev("lookup-bdev1"));
|
||||
|
||||
TEST_ASSERT_NULL(first_blockdev());
|
||||
|
||||
register_blockdev(bdev0);
|
||||
|
||||
TEST_ASSERT_PTR_EQ(lookup_blockdev("lookup-bdev0"), bdev0);
|
||||
TEST_ASSERT_PTR_EQ(first_blockdev(), bdev0);
|
||||
TEST_ASSERT_NULL(lookup_blockdev("lookup-bdev1"));
|
||||
|
||||
register_blockdev(bdev1);
|
||||
|
||||
TEST_ASSERT_PTR_EQ(lookup_blockdev("lookup-bdev0"), bdev0);
|
||||
TEST_ASSERT_PTR_EQ(lookup_blockdev("lookup-bdev1"), bdev1);
|
||||
|
||||
for (struct blockdev *bdev = first_blockdev(); bdev != NULL; bdev = next_blockdev(bdev)) {
|
||||
if (bdev == bdev0)
|
||||
found |= 1;
|
||||
if (bdev == bdev1)
|
||||
found |= 2;
|
||||
}
|
||||
TEST_ASSERT_EQ(found, 3);
|
||||
}
|
||||
|
||||
struct test_suite blockdev_suite = {
|
||||
.name = "blockdev",
|
||||
.test_cases = {
|
||||
{ "register_blockdev", test_register_blockdev, 0, "" },
|
||||
{ "mem_blockdev,1", test_mem_blockdev, 1, "" },
|
||||
{ "mem_blockdev,2", test_mem_blockdev, 2, "" },
|
||||
{ "mem_blockdev,4", test_mem_blockdev, 4, "" },
|
||||
{ "mem_blockdev,1024", test_mem_blockdev, 1024, "" },
|
||||
{ "blockdev_read_block,1", test_blockdev_read_block, 1, "" },
|
||||
{ "blockdev_read_block,2", test_blockdev_read_block, 2, "" },
|
||||
{ "blockdev_read_block,4", test_blockdev_read_block, 4, "" },
|
||||
{ "blockdev_read_block,1024", test_blockdev_read_block, 1024, "" },
|
||||
{ "blockdev_read_block,4096", test_blockdev_read_block, 4096, "" },
|
||||
{ "blockdev_read,1", test_blockdev_read, 1, "" },
|
||||
{ "blockdev_read,2", test_blockdev_read, 2, "" },
|
||||
{ "blockdev_read,4", test_blockdev_read, 4, "" },
|
||||
{ "blockdev_read,1024", test_blockdev_read, 1024, "" },
|
||||
{ "blockdev_read,4096", test_blockdev_read, 4096, "" },
|
||||
{ "blockdev_read,8192", test_blockdev_read, 8192, "" },
|
||||
{ "blockdev_write,1", test_blockdev_write, 1, "" },
|
||||
{ "blockdev_write,2", test_blockdev_write, 2, "" },
|
||||
{ "blockdev_write,4", test_blockdev_write, 4, "" },
|
||||
{ "blockdev_write,1024", test_blockdev_write, 1024, "" },
|
||||
{ "blockdev_write,4096", test_blockdev_write, 4096, "" },
|
||||
{ "blockdev_write,8192", test_blockdev_write, 8192, "" },
|
||||
{ "blockdev_set_buffer_alignment_read,2", test_blockdev_set_buffer_alignment_read, 2, "" },
|
||||
{ "blockdev_set_buffer_alignment_read,8", test_blockdev_set_buffer_alignment_read, 8, "" },
|
||||
{ "blockdev_set_buffer_alignment_read,1024", test_blockdev_set_buffer_alignment_read, 1024, "" },
|
||||
{ "blockdev_set_buffer_alignment_read,4096", test_blockdev_set_buffer_alignment_read, 4096, "" },
|
||||
TEST_CASE_LAST
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE(blockdev_suite);
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := blockdev
|
||||
|
||||
TEST_OBJS := \
|
||||
lib/libc/log2.o \
|
||||
$(LOCAL_DIR)/blockdev.o \
|
||||
$(LOCAL_DIR)/mem_blockdev.o \
|
||||
$(LOCAL_DIR)/subdev.o
|
||||
|
||||
TEST_SUPPORT_OBJS := \
|
||||
tests/unittest-main.o \
|
||||
$(LOCAL_DIR)/tests.o
|
||||
|
||||
TEST_CFLAGS := \
|
||||
-DCPU_CACHELINE_SIZE=32 \
|
||||
-DHOST_TEST=1
|
||||
Reference in new issue
Block a user