iBoot/lib/blockdev/debug.c

261 lines
6.6 KiB
C

/*
* 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;
}