aboutsummaryrefslogtreecommitdiffstats
path: root/hw/goldfish_nand.c
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 18:28:35 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 18:28:35 -0800
commitf721e3ac031f892af46f255a47d7f54a91317b30 (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /hw/goldfish_nand.c
parentbae1bc39312d5019bd9a5b8d840a529213a69a17 (diff)
downloadexternal_qemu-f721e3ac031f892af46f255a47d7f54a91317b30.zip
external_qemu-f721e3ac031f892af46f255a47d7f54a91317b30.tar.gz
external_qemu-f721e3ac031f892af46f255a47d7f54a91317b30.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'hw/goldfish_nand.c')
-rw-r--r--hw/goldfish_nand.c636
1 files changed, 0 insertions, 636 deletions
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c
deleted file mode 100644
index 61b075e..0000000
--- a/hw/goldfish_nand.c
+++ /dev/null
@@ -1,636 +0,0 @@
-/* Copyright (C) 2007-2008 The Android Open Source Project
-**
-** This software is licensed under the terms of the GNU General Public
-** License version 2, as published by the Free Software Foundation, and
-** may be copied, distributed, and modified under those terms.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-*/
-#include "qemu_file.h"
-#include "goldfish_nand_reg.h"
-#include "goldfish_nand.h"
-#include "android/utils/tempfile.h"
-#include "qemu_debug.h"
-#include "android/android.h"
-
-#define DEBUG 1
-#if DEBUG
-# define D(...) VERBOSE_PRINT(nand,__VA_ARGS__)
-# define D_ACTIVE VERBOSE_CHECK(nand)
-# define T(...) VERBOSE_PRINT(nand_limits,__VA_ARGS__)
-# define T_ACTIVE VERBOSE_CHECK(nand_limits)
-#else
-# define D(...) ((void)0)
-# define D_ACTIVE 0
-# define T(...) ((void)0)
-# define T_ACTIVE 0
-#endif
-
-/* lseek uses 64-bit offsets on Darwin. */
-/* prefer lseek64 on Linux */
-#ifdef __APPLE__
-# define llseek lseek
-#elif defined(__linux__)
-# define llseek lseek64
-#endif
-
-#define XLOG xlog
-
-static void
-xlog( const char* format, ... )
-{
- va_list args;
- va_start(args, format);
- fprintf(stderr, "NAND: ");
- vfprintf(stderr, format, args);
- va_end(args);
-}
-
-typedef struct {
- char* devname;
- size_t devname_len;
- char* data;
- int fd;
- uint32_t flags;
- uint32_t page_size;
- uint32_t extra_size;
- uint32_t erase_size;
- uint64_t size;
-} nand_dev;
-
-nand_threshold android_nand_write_threshold;
-nand_threshold android_nand_read_threshold;
-
-#ifdef CONFIG_NAND_THRESHOLD
-
-/* update a threshold, return 1 if limit is hit, 0 otherwise */
-static void
-nand_threshold_update( nand_threshold* t, uint32_t len )
-{
- if (t->counter < t->limit) {
- uint64_t avail = t->limit - t->counter;
- if (avail > len)
- avail = len;
-
- if (t->counter == 0) {
- T("%s: starting threshold counting to %lld",
- __FUNCTION__, t->limit);
- }
- t->counter += avail;
- if (t->counter >= t->limit) {
- /* threshold reach, send a signal to an external process */
- T( "%s: sending signal %d to pid %d !",
- __FUNCTION__, t->signal, t->pid );
-
- kill( t->pid, t->signal );
- }
- }
- return;
-}
-
-#define NAND_UPDATE_READ_THRESHOLD(len) \
- nand_threshold_update( &android_nand_read_threshold, (uint32_t)(len) )
-
-#define NAND_UPDATE_WRITE_THRESHOLD(len) \
- nand_threshold_update( &android_nand_write_threshold, (uint32_t)(len) )
-
-#else /* !NAND_THRESHOLD */
-
-#define NAND_UPDATE_READ_THRESHOLD(len) \
- do {} while (0)
-
-#define NAND_UPDATE_WRITE_THRESHOLD(len) \
- do {} while (0)
-
-#endif /* !NAND_THRESHOLD */
-
-static nand_dev *nand_devs = NULL;
-static uint32_t nand_dev_count = 0;
-
-typedef struct {
- uint32_t base;
-
- // register state
- uint32_t dev;
- uint32_t addr_low;
- uint32_t addr_high;
- uint32_t transfer_size;
- uint32_t data;
- uint32_t result;
-} nand_dev_state;
-
-/* update this everytime you change the nand_dev_state structure */
-#define NAND_DEV_STATE_SAVE_VERSION 1
-
-#define QFIELD_STRUCT nand_dev_state
-QFIELD_BEGIN(nand_dev_state_fields)
- QFIELD_INT32(dev),
- QFIELD_INT32(addr_low),
- QFIELD_INT32(addr_high),
- QFIELD_INT32(transfer_size),
- QFIELD_INT32(data),
- QFIELD_INT32(result),
-QFIELD_END
-
-static void nand_dev_state_save(QEMUFile* f, void* opaque)
-{
- nand_dev_state* s = opaque;
-
- qemu_put_struct(f, nand_dev_state_fields, s);
-}
-
-static int nand_dev_state_load(QEMUFile* f, void* opaque, int version_id)
-{
- nand_dev_state* s = opaque;
-
- if (version_id != NAND_DEV_STATE_SAVE_VERSION)
- return -1;
-
- return qemu_get_struct(f, nand_dev_state_fields, s);
-}
-
-
-static int do_read(int fd, void* buf, size_t size)
-{
- int ret;
- do {
- ret = read(fd, buf, size);
- } while (ret < 0 && errno == EINTR);
-
- return ret;
-}
-
-static int do_write(int fd, const void* buf, size_t size)
-{
- int ret;
- do {
- ret = write(fd, buf, size);
- } while (ret < 0 && errno == EINTR);
-
- return ret;
-}
-
-static uint32_t nand_dev_read_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len)
-{
- uint32_t len = total_len;
- size_t read_len = dev->erase_size;
- int eof = 0;
-
- NAND_UPDATE_READ_THRESHOLD(total_len);
-
- lseek(dev->fd, addr, SEEK_SET);
- while(len > 0) {
- if(read_len < dev->erase_size) {
- memset(dev->data, 0xff, dev->erase_size);
- read_len = dev->erase_size;
- eof = 1;
- }
- if(len < read_len)
- read_len = len;
- if(!eof) {
- read_len = do_read(dev->fd, dev->data, read_len);
- }
- pmemcpy(data, dev->data, read_len);
- data += read_len;
- len -= read_len;
- }
- return total_len;
-}
-
-static uint32_t nand_dev_write_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len)
-{
- uint32_t len = total_len;
- size_t write_len = dev->erase_size;
- int ret;
-
- NAND_UPDATE_WRITE_THRESHOLD(total_len);
-
- lseek(dev->fd, addr, SEEK_SET);
- while(len > 0) {
- if(len < write_len)
- write_len = len;
- vmemcpy(data, dev->data, write_len);
- ret = do_write(dev->fd, dev->data, write_len);
- if(ret < write_len) {
- XLOG("nand_dev_write_file, write failed: %s\n", strerror(errno));
- break;
- }
- data += write_len;
- len -= write_len;
- }
- return total_len - len;
-}
-
-static uint32_t nand_dev_erase_file(nand_dev *dev, uint64_t addr, uint32_t total_len)
-{
- uint32_t len = total_len;
- size_t write_len = dev->erase_size;
- int ret;
-
- lseek(dev->fd, addr, SEEK_SET);
- memset(dev->data, 0xff, dev->erase_size);
- while(len > 0) {
- if(len < write_len)
- write_len = len;
- ret = do_write(dev->fd, dev->data, write_len);
- if(ret < write_len) {
- XLOG( "nand_dev_write_file, write failed: %s\n", strerror(errno));
- break;
- }
- len -= write_len;
- }
- return total_len - len;
-}
-
-/* this is a huge hack required to make the PowerPC emulator binary usable
- * on Mac OS X. If you define this function as 'static', the emulated kernel
- * will panic when attempting to mount the /data partition.
- *
- * worse, if you do *not* define the function as static on Linux-x86, the
- * emulated kernel will also panic !?
- *
- * I still wonder if this is a compiler bug, or due to some nasty thing the
- * emulator does with CPU registers during execution of the translated code.
- */
-#if !(defined __APPLE__ && defined __powerpc__)
-static
-#endif
-uint32_t nand_dev_do_cmd(nand_dev_state *s, uint32_t cmd)
-{
- uint32_t size;
- uint64_t addr;
- nand_dev *dev;
-
- addr = s->addr_low | ((uint64_t)s->addr_high << 32);
- size = s->transfer_size;
- if(s->dev >= nand_dev_count)
- return 0;
- dev = nand_devs + s->dev;
-
- switch(cmd) {
- case NAND_CMD_GET_DEV_NAME:
- if(size > dev->devname_len)
- size = dev->devname_len;
- pmemcpy(s->data, dev->devname, size);
- return size;
- case NAND_CMD_READ:
- if(addr >= dev->size)
- return 0;
- if(size + addr > dev->size)
- size = dev->size - addr;
- if(dev->fd >= 0)
- return nand_dev_read_file(dev, s->data, addr, size);
- pmemcpy(s->data, &dev->data[addr], size);
- return size;
- case NAND_CMD_WRITE:
- if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
- return 0;
- if(addr >= dev->size)
- return 0;
- if(size + addr > dev->size)
- size = dev->size - addr;
- if(dev->fd >= 0)
- return nand_dev_write_file(dev, s->data, addr, size);
- vmemcpy(s->data, &dev->data[addr], size);
- return size;
- case NAND_CMD_ERASE:
- if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
- return 0;
- if(addr >= dev->size)
- return 0;
- if(size + addr > dev->size)
- size = dev->size - addr;
- if(dev->fd >= 0)
- return nand_dev_erase_file(dev, addr, size);
- memset(&dev->data[addr], 0xff, size);
- return size;
- case NAND_CMD_BLOCK_BAD_GET: // no bad block support
- return 0;
- case NAND_CMD_BLOCK_BAD_SET:
- if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
- return 0;
- return 0;
- default:
- cpu_abort(cpu_single_env, "nand_dev_do_cmd: Bad command %x\n", cmd);
- return 0;
- }
-}
-
-/* I/O write */
-static void nand_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
-{
- nand_dev_state *s = (nand_dev_state *)opaque;
-
- offset -= s->base;
- switch (offset) {
- case NAND_DEV:
- s->dev = value;
- if(s->dev >= nand_dev_count) {
- cpu_abort(cpu_single_env, "nand_dev_write: Bad dev %x\n", value);
- }
- break;
- case NAND_ADDR_HIGH:
- s->addr_high = value;
- break;
- case NAND_ADDR_LOW:
- s->addr_low = value;
- break;
- case NAND_TRANSFER_SIZE:
- s->transfer_size = value;
- break;
- case NAND_DATA:
- s->data = value;
- break;
- case NAND_COMMAND:
- s->result = nand_dev_do_cmd(s, value);
- break;
- default:
- cpu_abort(cpu_single_env, "nand_dev_write: Bad offset %x\n", offset);
- break;
- }
-}
-
-/* I/O read */
-static uint32_t nand_dev_read(void *opaque, target_phys_addr_t offset)
-{
- nand_dev_state *s = (nand_dev_state *)opaque;
- nand_dev *dev;
-
- offset -= s->base;
- switch (offset) {
- case NAND_VERSION:
- return NAND_VERSION_CURRENT;
- case NAND_NUM_DEV:
- return nand_dev_count;
- case NAND_RESULT:
- return s->result;
- }
-
- if(s->dev >= nand_dev_count)
- return 0;
-
- dev = nand_devs + s->dev;
-
- switch (offset) {
- case NAND_DEV_FLAGS:
- return dev->flags;
-
- case NAND_DEV_NAME_LEN:
- return dev->devname_len;
-
- case NAND_DEV_PAGE_SIZE:
- return dev->page_size;
-
- case NAND_DEV_EXTRA_SIZE:
- return dev->extra_size;
-
- case NAND_DEV_ERASE_SIZE:
- return dev->erase_size;
-
- case NAND_DEV_SIZE_LOW:
- return (uint32_t)dev->size;
-
- case NAND_DEV_SIZE_HIGH:
- return (uint32_t)(dev->size >> 32);
-
- default:
- cpu_abort(cpu_single_env, "nand_dev_read: Bad offset %x\n", offset);
- return 0;
- }
-}
-
-static CPUReadMemoryFunc *nand_dev_readfn[] = {
- nand_dev_read,
- nand_dev_read,
- nand_dev_read
-};
-
-static CPUWriteMemoryFunc *nand_dev_writefn[] = {
- nand_dev_write,
- nand_dev_write,
- nand_dev_write
-};
-
-/* initialize the QFB device */
-void nand_dev_init(uint32_t base)
-{
- int iomemtype;
- static int instance_id = 0;
- nand_dev_state *s;
-
- s = (nand_dev_state *)qemu_mallocz(sizeof(nand_dev_state));
- iomemtype = cpu_register_io_memory(0, nand_dev_readfn, nand_dev_writefn, s);
- cpu_register_physical_memory(base, 0x00000fff, iomemtype);
- s->base = base;
-
- register_savevm( "nand_dev", instance_id++, NAND_DEV_STATE_SAVE_VERSION,
- nand_dev_state_save, nand_dev_state_load, s);
-}
-
-static int arg_match(const char *a, const char *b, size_t b_len)
-{
- while(*a && b_len--) {
- if(*a++ != *b++)
- return 0;
- }
- return b_len == 0;
-}
-
-void nand_add_dev(const char *arg)
-{
- uint64_t dev_size = 0;
- const char *next_arg;
- const char *value;
- size_t arg_len, value_len;
- nand_dev *new_devs, *dev;
- char *devname = NULL;
- size_t devname_len = 0;
- char *initfilename = NULL;
- char *rwfilename = NULL;
- int initfd = -1;
- int rwfd = -1;
- int read_only = 0;
- int pad;
- ssize_t read_size;
- uint32_t page_size = 2048;
- uint32_t extra_size = 64;
- uint32_t erase_pages = 64;
-
- while(arg) {
- next_arg = strchr(arg, ',');
- value = strchr(arg, '=');
- if(next_arg != NULL) {
- arg_len = next_arg - arg;
- next_arg++;
- if(value >= next_arg)
- value = NULL;
- }
- else
- arg_len = strlen(arg);
- if(value != NULL) {
- size_t new_arg_len = value - arg;
- value_len = arg_len - new_arg_len - 1;
- arg_len = new_arg_len;
- value++;
- }
- else
- value_len = 0;
-
- if(devname == NULL) {
- if(value != NULL)
- goto bad_arg_and_value;
- devname_len = arg_len;
- devname = malloc(arg_len);
- if(devname == NULL)
- goto out_of_memory;
- memcpy(devname, arg, arg_len);
- }
- else if(value == NULL) {
- if(arg_match("readonly", arg, arg_len)) {
- read_only = 1;
- }
- else {
- XLOG("bad arg: %.*s\n", arg_len, arg);
- exit(1);
- }
- }
- else {
- if(arg_match("size", arg, arg_len)) {
- char *ep;
- dev_size = strtoull(value, &ep, 0);
- if(ep != value + value_len)
- goto bad_arg_and_value;
- }
- else if(arg_match("pagesize", arg, arg_len)) {
- char *ep;
- page_size = strtoul(value, &ep, 0);
- if(ep != value + value_len)
- goto bad_arg_and_value;
- }
- else if(arg_match("extrasize", arg, arg_len)) {
- char *ep;
- extra_size = strtoul(value, &ep, 0);
- if(ep != value + value_len)
- goto bad_arg_and_value;
- }
- else if(arg_match("erasepages", arg, arg_len)) {
- char *ep;
- erase_pages = strtoul(value, &ep, 0);
- if(ep != value + value_len)
- goto bad_arg_and_value;
- }
- else if(arg_match("initfile", arg, arg_len)) {
- initfilename = malloc(value_len + 1);
- if(initfilename == NULL)
- goto out_of_memory;
- memcpy(initfilename, value, value_len);
- initfilename[value_len] = '\0';
- }
- else if(arg_match("file", arg, arg_len)) {
- rwfilename = malloc(value_len + 1);
- if(rwfilename == NULL)
- goto out_of_memory;
- memcpy(rwfilename, value, value_len);
- rwfilename[value_len] = '\0';
- }
- else {
- goto bad_arg_and_value;
- }
- }
-
- arg = next_arg;
- }
-
- if (rwfilename == NULL) {
- /* we create a temporary file to store everything */
- TempFile* tmp = tempfile_create();
-
- if (tmp == NULL) {
- XLOG("could not create temp file for %.*s NAND disk image: %s",
- devname_len, devname, strerror(errno));
- exit(1);
- }
- rwfilename = (char*) tempfile_path(tmp);
- if (VERBOSE_CHECK(init))
- dprint( "mapping '%.*s' NAND image to %s", devname_len, devname, rwfilename);
- }
-
- if(rwfilename) {
- rwfd = open(rwfilename, O_BINARY | (read_only ? O_RDONLY : O_RDWR));
- if(rwfd < 0 && read_only) {
- XLOG("could not open file %s, %s\n", rwfilename, strerror(errno));
- exit(1);
- }
- /* this could be a writable temporary file. use atexit_close_fd to ensure
- * that it is properly cleaned up at exit on Win32
- */
- if (!read_only)
- atexit_close_fd(rwfd);
- }
-
- if(initfilename) {
- initfd = open(initfilename, O_BINARY | O_RDONLY);
- if(initfd < 0) {
- XLOG("could not open file %s, %s\n", initfilename, strerror(errno));
- exit(1);
- }
- if(dev_size == 0) {
- dev_size = lseek(initfd, 0, SEEK_END);
- lseek(initfd, 0, SEEK_SET);
- }
- }
-
- new_devs = realloc(nand_devs, sizeof(nand_devs[0]) * (nand_dev_count + 1));
- if(new_devs == NULL)
- goto out_of_memory;
- nand_devs = new_devs;
- dev = &new_devs[nand_dev_count];
-
- dev->page_size = page_size;
- dev->extra_size = extra_size;
- dev->erase_size = erase_pages * (page_size + extra_size);
- pad = dev_size % dev->erase_size;
- if (pad != 0) {
- dev_size += (dev->erase_size - pad);
- XLOG("rounding devsize up to a full eraseunit, now %llx\n", dev_size);
- }
- dev->devname = devname;
- dev->devname_len = devname_len;
- dev->size = dev_size;
- dev->data = malloc(dev->erase_size);
- if(dev->data == NULL)
- goto out_of_memory;
- dev->flags = read_only ? NAND_DEV_FLAG_READ_ONLY : 0;
-
- if (initfd >= 0) {
- do {
- read_size = do_read(initfd, dev->data, dev->erase_size);
- if(read_size < 0) {
- XLOG("could not read file %s, %s\n", initfilename, strerror(errno));
- exit(1);
- }
- if(do_write(rwfd, dev->data, read_size) != read_size) {
- XLOG("could not write file %s, %s\n", initfilename, strerror(errno));
- exit(1);
- }
- } while(read_size == dev->erase_size);
- close(initfd);
- }
- dev->fd = rwfd;
-
- nand_dev_count++;
-
- return;
-
-out_of_memory:
- XLOG("out of memory\n");
- exit(1);
-
-bad_arg_and_value:
- XLOG("bad arg: %.*s=%.*s\n", arg_len, arg, value_len, value);
- exit(1);
-}
-