qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller.


From: Patrick Jackson
Subject: [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller.
Date: Mon, 22 Aug 2011 05:39:04 -0400


The paths to the system images must be specified from the command line as follows.

-global goldfish_nand.system_init_path=/path/to/initial/system/image -global goldfish_nand.user_data_path=/path/to/user/data

Signed-off-by: Patrick Jackson <address@hidden>
---
 Makefile.target        |    2 +-
 hw/android_arm.c       |    2 +
 hw/goldfish_device.c   | 1398 ++++++++++++++++++++++++++++++++++++++++++++++++
 hw/goldfish_device.h   |  205 +++++++
 hw/goldfish_nand.c     |  914 +++++++++++++++++++++++++++++++
 hw/goldfish_nand.h     |   29 +
 hw/goldfish_nand_reg.h |   54 ++
 7 files changed, 2603 insertions(+), 1 deletions(-)
 create mode 100644 hw/goldfish_nand.c
 create mode 100644 hw/goldfish_nand.h
 create mode 100644 hw/goldfish_nand_reg.h

diff --git a/Makefile.target b/Makefile.target
index ec8d189..ea9a741 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -361,7 +361,7 @@ obj-arm-y += vexpress.o
 obj-arm-y += strongarm.o
 obj-arm-y += collie.o
 obj-arm-y += android_arm.o goldfish_device.o goldfish_interrupt.o goldfish_timer.o
-obj-arm-y += goldfish_tty.o
+obj-arm-y += goldfish_tty.o goldfish_nand.o
 
 obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
 obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/hw/android_arm.c b/hw/android_arm.c
index 89b07d1..bf49b74 100644
--- a/hw/android_arm.c
+++ b/hw/android_arm.c
@@ -65,6 +65,8 @@ static void android_arm_init_(ram_addr_t ram_size,
         }
     }
 
+    goldfish_nand_create(gbus);
+
     info.ram_size        = ram_size;
     info.kernel_filename = kernel_filename;
     info.kernel_cmdline  = kernel_cmdline;
diff --git a/hw/goldfish_device.c b/hw/goldfish_device.c
index 39f5247..b34ee92 100644
--- a/hw/goldfish_device.c
+++ b/hw/goldfish_device.c
@@ -292,3 +292,1401 @@ static void goldfish_register_devices(void)
 
 device_init(goldfish_register_devices)
 
+#define  D(...)  ((void)0)
+
+#ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include "windows.h"
+#  include "shlobj.h"
+#else
+#  include <unistd.h>
+#  include <sys/stat.h>
+#endif
+
+/** FORMATTED BUFFER PRINTING
+ **
+ **  bufprint() allows your to easily and safely append formatted string
+ **  content to a given bounded character buffer, in a way that is easier
+ **  to use than raw snprintf()
+ **
+ **  'buffer'  is the start position in the buffer,
+ **  'buffend' is the end of the buffer, the function assumes (buffer <= buffend)
+ **  'format'  is a standard printf-style format string, followed by any number
+ **            of formatting arguments
+ **
+ **  the function returns the next position in the buffer if everything fits
+ **  in it. in case of overflow or formatting error, it will always return "buffend"
+ **
+ **  this allows you to chain several calls to bufprint() and only check for
+ **  overflow at the end, for exemple:
+ **
+ **     char   buffer[1024];
+ **     char*  p   = buffer;
+ **     char*  end = p + sizeof(buffer);
+ **
+ **     p = bufprint(p, end, "%s/%s", first, second);
+ **     p = bufprint(p, end, "/%s", third);
+ **     if (p >= end) ---> overflow
+ **
+ **  as a convenience, the appended string is zero-terminated if there is no overflow.
+ **  (this means that even if p >= end, the content of "buffer" is zero-terminated)
+ **
+ **  vbufprint() is a variant that accepts a va_list argument
+ **/
+
+static char*
+vbufprint( char*        buffer,
+           char*        buffer_end,
+           const char*  fmt,
+           va_list      args )
+{
+    int  len = vsnprintf( buffer, buffer_end - buffer, fmt, args );
+    if (len < 0 || buffer+len >= buffer_end) {
+        if (buffer < buffer_end)
+            buffer_end[-1] = 0;
+        return buffer_end;
+    }
+    return buffer + len;
+}
+
+static char*
+bufprint(char*  buffer, char*  end, const char*  fmt, ... )
+{
+    va_list  args;
+    char*    result;
+
+    va_start(args, fmt);
+    result = vbufprint(buffer, end, fmt, args);
+    va_end(args);
+    return  result;
+}
+
+/** USEFUL DIRECTORY SUPPORT
+ **
+ **  bufprint_add_dir() appends the application's directory to a given bounded buffer
+ **
+ **  bufprint_config_path() appends the applications' user-specific configuration directory
+ **  to a bounded buffer. on Unix this is usually ~/.android, and something a bit more
+ **  complex on Windows
+ **
+ **  bufprint_config_file() appends the name of a file or directory relative to the
+ **  user-specific configuration directory to a bounded buffer. this really is equivalent
+ **  to concat-ing the config path + path separator + 'suffix'
+ **
+ **  bufprint_temp_dir() appends the temporary directory's path to a given bounded buffer
+ **
+ **  bufprint_temp_file() appens the name of a file or directory relative to the
+ **  temporary directory. equivalent to concat-ing the temp path + path separator + 'suffix'
+ **/
+
+#ifdef __linux__
+/*static char*
+bufprint_app_dir(char*  buff, char*  end)
+{
+    char   path[1024];
+    int    len;
+    char*  x;
+
+    len = readlink("/proc/self/exe", path, sizeof(path));
+    if (len <= 0 || len >= (int)sizeof(path)) goto Fail;
+    path[len] = 0;
+
+    x = strrchr(path, '/');
+    if (x == 0) goto Fail;
+    *x = 0;
+
+    return bufprint(buff, end, "%s", path);
+Fail:
+    fprintf(stderr,"cannot locate application directory\n");
+    exit(1);
+    return end;
+}*/
+
+#elif defined(__APPLE__)
+/* the following hack is needed in order to build with XCode 3.1
+ * don't ask me why, but it seems that there were changes in the
+ * GCC compiler that we don't have in our pre-compiled version
+ */
+#ifndef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
+#define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ MAC_OS_X_VERSION_10_4
+#endif
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+static char*
+bufprint_app_dir(char*  buff, char*  end)
+{
+    ProcessSerialNumber psn;
+    CFDictionaryRef     dict;
+    CFStringRef         value;
+    char                s[PATH_MAX];
+    char*               x;
+
+    GetCurrentProcess(&psn);
+    dict  = ProcessInformationCopyDictionary(&psn, 0xffffffff);
+    value = (CFStringRef)CFDictionaryGetValue(dict,
+                                             CFSTR("CFBundleExecutable"));
+    CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
+    x = strrchr(s, '/');
+    if (x == 0) goto fail;
+    *x = 0;
+
+    return bufprint(buff, end, "%s", s);
+fail:
+    fprintf(stderr,"cannot locate application directory\n");
+    exit(1);
+    return end;
+}
+#elif defined _WIN32
+static char*
+bufprint_app_dir(char*  buff, char*  end)
+{
+    char   appDir[MAX_PATH];
+ int    len;
+ char*  sep;
+
+    len = GetModuleFileName( 0, appDir, sizeof(appDir)-1 );
+ if (len == 0) {
+ fprintf(stderr, "PANIC CITY!!\n");
+ exit(1);
+ }
+ if (len >= (int)sizeof(appDir)) {
+ len = sizeof(appDir)-1;
+    appDir[len] = 0;
+    }
+
+ sep = strrchr(appDir, '\\');
+ if (sep)
+  *sep = 0;
+
+    return bufprint(buff, end, "%s", appDir);
+}
+#else
+static char*
+bufprint_app_dir(char*  buff, char*  end)
+{
+    return bufprint(buff, end, ".");
+}
+#endif
+
+#define  _ANDROID_PATH   ".android"
+
+/*static char*
+bufprint_config_path(char*  buff, char*  end)
+{
+#ifdef _WIN32
+    const char*  home = getenv("ANDROID_SDK_HOME");
+    if (home != NULL) {
+        return bufprint(buff, end, "%s\\%s", home, _ANDROID_PATH );
+    } else {
+        char  path[MAX_PATH];
+
+        SHGetFolderPath( NULL, CSIDL_PROFILE,
+                         NULL, 0, path);
+
+        return bufprint(buff, end, "%s\\%s", path, _ANDROID_PATH );
+    }
+#else
+    const char*  home = getenv("ANDROID_SDK_HOME");
+    if (home == NULL)
+        home = getenv("HOME");
+    if (home == NULL)
+        home = "/tmp";
+    return bufprint(buff, end, "%s/%s", home, _ANDROID_PATH );
+#endif
+}*/
+
+/*static char*
+bufprint_config_file(char*  buff, char*  end, const char*  suffix)
+{
+    char*   p;
+    p = bufprint_config_path(buff, end);
+    p = bufprint(p, end, PATH_SEP "%s", suffix);
+    return p;
+}*/
+
+static char*
+bufprint_temp_dir(char*  buff, char*  end)
+{
+#ifdef _WIN32
+    char   path[MAX_PATH];
+    DWORD  retval;
+
+    retval = GetTempPath( sizeof(path), path );
+    if (retval > sizeof(path) || retval == 0) {
+        D( "can't locate TEMP directory" );
+        strncpy(path, "C:\\Temp", sizeof(path) );
+    }
+    strncat( path, "\\AndroidEmulator", sizeof(path)-1 );
+    path_mkdir(path, 0744);
+
+    return  bufprint(buff, end, "%s", path);
+#else
+    char path[MAX_PATH];
+    const char*  tmppath = getenv("ANDROID_TMP");
+    if (!tmppath) {
+        const char* user = getenv("USER");
+        if (user == NULL || user[0] == '\0')
+            user = "unknown";
+
+        snprintf(path, sizeof path, "/tmp/android-%s", user);
+        tmppath = path;
+    }
+    mkdir(tmppath, 0744);
+    return  bufprint(buff, end, "%s", tmppath );
+#endif
+}
+
+static char*
+bufprint_temp_file(char*  buff, char*  end, const char*  suffix)
+{
+    char*  p;
+    p = bufprint_temp_dir(buff, end);
+    p = bufprint(p, end, PATH_SEP "%s", suffix);
+    return p;
+}
+
+/* Tempfile support */
+struct TempFile
+{
+    const char*  name;
+    TempFile*    next;
+};
+
+static void       tempfile_atexit(void);
+static TempFile*  _all_tempfiles;
+
+TempFile*
+tempfile_create( void )
+{
+    TempFile*    tempfile;
+    const char*  tempname = NULL;
+
+#ifdef _WIN32
+    char  temp_namebuff[MAX_PATH];
+    char  temp_dir[MAX_PATH];
+    char  *p = temp_dir, *end = p + sizeof(temp_dir);
+    UINT  retval;
+
+    p = bufprint_temp_dir( p, end );
+    if (p >= end) {
+        D( "TEMP directory path is too long" );
+        return NULL;
+    }
+
+    retval = GetTempFileName(temp_dir, "TMP", 0, temp_namebuff);
+    if (retval == 0) {
+        D( "can't create temporary file in '%s'", temp_dir );
+        return NULL;
+    }
+
+    tempname = temp_namebuff;
+#else
+#define  TEMPLATE  "/tmp/.android-emulator-XXXXXX"
+    int   tempfd = -1;
+    char  template[512];
+    char  *p = template, *end = p + sizeof(template);
+
+    p = bufprint_temp_file( p, end, "emulator-XXXXXX" );
+    if (p >= end) {
+        D( "Xcannot create temporary file in /tmp/android !!" );
+        return NULL;
+    }
+
+    D( "template: %s", template );
+    tempfd = mkstemp( template );
+    if (tempfd < 0) {
+        D("cannot create temporary file in /tmp/android !!");
+        return NULL;
+    }
+    close(tempfd);
+    tempname = template;
+#endif
+    tempfile = malloc( sizeof(*tempfile) + strlen(tempname) + 1 );
+    tempfile->name = (char*)(tempfile + 1);
+    strcpy( (char*)tempfile->name, tempname );
+
+    tempfile->next = _all_tempfiles;
+    _all_tempfiles = tempfile;
+
+    if ( !tempfile->next ) {
+        atexit( tempfile_atexit );
+    }
+
+    return tempfile;
+}
+
+const char*
+tempfile_path(TempFile*  temp)
+{
+    return temp ? temp->name : NULL;
+}
+
+void
+tempfile_close(TempFile*  tempfile)
+{
+#ifdef _WIN32
+    DeleteFile(tempfile->name);
+#else
+    unlink(tempfile->name);
+#endif
+}
+
+/** TEMP FILE CLEANUP
+ **
+ **/
+
+/* we don't expect to use many temporary files */
+#define MAX_ATEXIT_FDS  16
+
+typedef struct {
+    int   count;
+    int   fds[ MAX_ATEXIT_FDS ];
+} AtExitFds;
+
+static void
+atexit_fds_add( AtExitFds*  t, int  fd )
+{
+    if (t->count < MAX_ATEXIT_FDS)
+        t->fds[t->count++] = fd;
+    else {
+        D("%s: over %d calls. Program exit may not cleanup all temporary files",
+            __FUNCTION__, MAX_ATEXIT_FDS);
+    }
+}
+
+static void
+atexit_fds_del( AtExitFds*  t, int  fd )
+{
+    int  nn;
+    for (nn = 0; nn < t->count; nn++)
+        if (t->fds[nn] == fd) {
+            /* move the last element to the current position */
+            t->count  -= 1;
+            t->fds[nn] = t->fds[t->count];
+            break;
+        }
+}
+
+static void
+atexit_fds_close_all( AtExitFds*  t )
+{
+    int  nn;
+    for (nn = 0; nn < t->count; nn++)
+        close(t->fds[nn]);
+}
+
+static AtExitFds   _atexit_fds[1];
+
+void
+atexit_close_fd(int  fd)
+{
+    if (fd >= 0)
+        atexit_fds_add(_atexit_fds, fd);
+}
+
+void
+atexit_close_fd_remove(int  fd)
+{
+    if (fd >= 0)
+        atexit_fds_del(_atexit_fds, fd);
+}
+
+static void
+tempfile_atexit( void )
+{
+    TempFile*  tempfile;
+
+    atexit_fds_close_all( _atexit_fds );
+
+    for (tempfile = _all_tempfiles; tempfile; tempfile = tempfile->next)
+        tempfile_close(tempfile);
+}
+
+#ifdef _WIN32
+#  include <process.h>
+#  include <windows.h>
+#  include <tlhelp32.h>
+#else
+#  include <sys/types.h>
+#  include <unistd.h>
+#  include <signal.h>
+#endif
+
+
+#ifndef CHECKED
+#  ifdef _WIN32
+#    define   CHECKED(ret, call)    (ret) = (call)
+#  else
+#    define   CHECKED(ret, call)    do { (ret) = (call); } while ((ret) < 0 && errno == EINTR)
+#  endif
+#endif
+
+/** FILE LOCKS SUPPORT
+ **
+ ** a FileLock is useful to prevent several emulator instances from using the same
+ ** writable file (e.g. the userdata.img disk images).
+ **
+ ** create a FileLock object with filelock_create(), ithis function should return NULL
+ ** only if the corresponding file path could not be locked.
+ **
+ ** all file locks are automatically released and destroyed when the program exits.
+ ** the filelock_lock() function can also detect stale file locks that can linger
+ ** when the emulator crashes unexpectedly, and will happily clean them for you
+ **
+ **  here's how it works, three files are used:
+ **     file  - the data file accessed by the emulator
+ **     lock  - a lock file  (file + '.lock')
+ **     temp  - a temporary file make unique with mkstemp
+ **
+ **  when locking:
+ **      create 'temp' and store our pid in it
+ **      attemp to link 'lock' to 'temp'
+ **         if the link succeeds, we obtain the lock
+ **      unlink 'temp'
+ **
+ **  when unlocking:
+ **      unlink 'lock'
+ **
+ **
+ **  on Windows, 'lock' is a directory name. locking is equivalent to
+ **  creating it...
+ **
+ **/
+
+struct FileLock
+{
+  const char*  file;
+  const char*  lock;
+  char*        temp;
+  int          locked;
+  FileLock*    next;
+};
+
+/* used to cleanup all locks at emulator exit */
+static FileLock*   _all_filelocks;
+
+
+#define  LOCK_NAME   ".lock"
+#define  TEMP_NAME   ".tmp-XXXXXX"
+
+#ifdef _WIN32
+#define  PIDFILE_NAME  "pid"
+#endif
+
+/* returns 0 on success, -1 on failure */
+static int
+filelock_lock( FileLock*  lock )
+{
+    int    ret;
+#ifdef _WIN32
+    int  pidfile_fd = -1;
+
+    ret = _mkdir( lock->lock );
+    if (ret < 0) {
+        if (errno == ENOENT) {
+            D( "could not access directory '%s', check path elements", lock->lock );
+            return -1;
+        } else if (errno != EEXIST) {
+            D( "_mkdir(%s): %s", lock->lock, strerror(errno) );
+            return -1;
+        }
+
+        /* if we get here, it's because the .lock directory already exists */
+        /* check to see if there is a pid file in it                       */
+        D("directory '%s' already exist, waiting a bit to ensure that no other emulator instance is starting", lock->lock );
+        {
+            int  _sleep = 200;
+            int  tries;
+
+            for ( tries = 4; tries > 0; tries-- )
+            {
+                pidfile_fd = open( lock->temp, O_RDONLY );
+
+                if (pidfile_fd >= 0)
+                    break;
+
+                Sleep( _sleep );
+                _sleep *= 2;
+            }
+        }
+
+        if (pidfile_fd < 0) {
+            D( "no pid file in '%s', assuming stale directory", lock->lock );
+        }
+        else
+        {
+            /* read the pidfile, and check wether the corresponding process is still running */
+            char            buf[16];
+            int             len, lockpid;
+            HANDLE          processSnapshot;
+            PROCESSENTRY32  pe32;
+            int             is_locked = 0;
+
+            len = read( pidfile_fd, buf, sizeof(buf)-1 );
+            if (len < 0) {
+                D( "could not read pid file '%s'", lock->temp );
+                close( pidfile_fd );
+                return -1;
+            }
+            buf[len] = 0;
+            lockpid  = atoi(buf);
+
+            /* PID 0 is the IDLE process, and 0 is returned in case of invalid input */
+            if (lockpid == 0)
+                lockpid = -1;
+
+            close( pidfile_fd );
+
+            pe32.dwSize     = sizeof( PROCESSENTRY32 );
+            processSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
+
+            if ( processSnapshot == INVALID_HANDLE_VALUE ) {
+                D( "could not retrieve the list of currently active processes\n" );
+                is_locked = 1;
+            }
+            else if ( !Process32First( processSnapshot, &pe32 ) )
+            {
+                D( "could not retrieve first process id\n" );
+                CloseHandle( processSnapshot );
+                is_locked = 1;
+            }
+            else
+            {
+                do {
+                    if (pe32.th32ProcessID == lockpid) {
+                        is_locked = 1;
+                        break;
+                    }
+                } while (Process32Next( processSnapshot, &pe32 ) );
+
+                CloseHandle( processSnapshot );
+            }
+
+            if (is_locked) {
+                D( "the file '%s' is locked by process ID %d\n", lock->file, lockpid );
+                return -1;
+            }
+        }
+    }
+
+    /* write our PID into the pid file */
+    pidfile_fd = open( lock->temp, O_WRONLY | O_CREAT | O_TRUNC );
+    if (pidfile_fd < 0) {
+        if (errno == EACCES) {
+            if ( path_delete_file( lock->temp ) < 0 ) {
+                D( "could not remove '%s': %s\n", lock->temp, strerror(errno) );
+                return -1;
+            }
+            pidfile_fd = open( lock->temp, O_WRONLY | O_CREAT | O_TRUNC );
+        }
+        if (pidfile_fd < 0) {
+            D( "could not create '%s': %s\n", lock->temp, strerror(errno) );
+            return -1;
+        }
+    }
+
+    {
+        char  buf[16];
+        sprintf( buf, "%ld", GetCurrentProcessId() );
+        ret = write( pidfile_fd, buf, strlen(buf) );
+        close(pidfile_fd);
+        if (ret < 0) {
+            D( "could not write PID to '%s'\n", lock->temp );
+            return -1;
+        }
+    }
+
+    lock->locked = 1;
+    return 0;
+#else
+    int    temp_fd = -1;
+    int    lock_fd = -1;
+    int    rc, tries, _sleep;
+    FILE*  f = NULL;
+    char   pid[8];
+    struct stat  st_temp;
+
+    strcpy( lock->temp, lock->file );
+    strcat( lock->temp, TEMP_NAME );
+    temp_fd = mkstemp( lock->temp );
+
+    if (temp_fd < 0) {
+        D("cannot create locking temp file '%s'", lock->temp );
+        goto Fail;
+    }
+
+    sprintf( pid, "%d", getpid() );
+    ret = write( temp_fd, pid, strlen(pid)+1 );
+    if (ret < 0) {
+        D( "cannot write to locking temp file '%s'", lock->temp);
+        goto Fail;
+    }
+    close( temp_fd );
+    temp_fd = -1;
+
+    CHECKED(rc, lstat( lock->temp, &st_temp ));
+    if (rc < 0) {
+        D( "can't properly stat our locking temp file '%s'", lock->temp );
+        goto Fail;
+    }
+
+    /* now attempt to link the temp file to the lock file */
+    _sleep = 0;
+    for ( tries = 4; tries > 0; tries-- )
+    {
+        struct stat  st_lock;
+        int          rc;
+
+        if (_sleep > 0) {
+            if (_sleep > 2000000) {
+                D( "cannot acquire lock file '%s'", lock->lock );
+                goto Fail;
+            }
+            usleep( _sleep );
+        }
+        _sleep += 200000;
+
+        /* the return value of link() is buggy on NFS */
+        CHECKED(rc, link( lock->temp, lock->lock ));
+
+        CHECKED(rc, lstat( lock->lock, &st_lock ));
+        if (rc == 0 &&
+            st_temp.st_rdev == st_lock.st_rdev &&
+            st_temp.st_ino  == st_lock.st_ino  )
+        {
+            /* SUCCESS */
+            lock->locked = 1;
+            CHECKED(rc, unlink( lock->temp ));
+            return 0;
+        }
+
+        /* if we get there, it means that the link() call failed */
+        /* check the lockfile to see if it is stale              */
+        if (rc == 0) {
+            char    buf[16];
+            time_t  now;
+            int     lockpid = 0;
+            int     lockfd;
+            int     stale = 2;  /* means don't know */
+            struct stat  st;
+
+            CHECKED(rc, time( &now));
+            st.st_mtime = now - 120;
+
+            CHECKED(lockfd, open( lock->lock,O_RDONLY ));
+            if ( lockfd >= 0 ) {
+                int  len;
+
+                CHECKED(len, read( lockfd, buf, sizeof(buf)-1 ));
+                buf[len] = 0;
+                lockpid = atoi(buf);
+
+                CHECKED(rc, fstat( lockfd, &st ));
+                if (rc == 0)
+                  now = st.st_atime;
+
+                CHECKED(rc, close(lockfd));
+            }
+            /* if there is a PID, check that it is still alive */
+            if (lockpid > 0) {
+                CHECKED(rc, kill( lockpid, 0 ));
+                if (rc == 0 || errno == EPERM) {
+                    stale = 0;
+                } else if (rc < 0 && errno == ESRCH) {
+                    stale = 1;
+                }
+            }
+            if (stale == 2) {
+                /* no pid, stale if the file is older than 1 minute */
+                stale = (now >= st.st_mtime + 60);
+            }
+
+            if (stale) {
+                D( "removing stale lockfile '%s'", lock->lock );
+                CHECKED(rc, unlink( lock->lock ));
+                _sleep = 0;
+                tries++;
+            }
+        }
+    }
+    D("file '%s' is already in use by another process", lock->file );
+
+Fail:
+    if (f)
+        fclose(f);
+
+    if (temp_fd >= 0) {
+        close(temp_fd);
+    }
+
+    if (lock_fd >= 0) {
+        close(lock_fd);
+    }
+
+    unlink( lock->lock );
+    unlink( lock->temp );
+    return -1;
+#endif
+}
+
+void
+filelock_release( FileLock*  lock )
+{
+    if (lock->locked) {
+#ifdef _WIN32
+        path_delete_file( (char*)lock->temp );
+        rmdir( (char*)lock->lock );
+#else
+        unlink( (char*)lock->lock );
+#endif
+        lock->locked = 0;
+    }
+}
+
+static void
+filelock_atexit( void )
+{
+  FileLock*  lock;
+
+  for (lock = _all_filelocks; lock != NULL; lock = lock->next)
+     filelock_release( lock );
+}
+
+/* create a file lock */
+FileLock*
+filelock_create( const char*  file )
+{
+    int    file_len = strlen(file);
+    int    lock_len = file_len + sizeof(LOCK_NAME);
+#ifdef _WIN32
+    int    temp_len = lock_len + 1 + sizeof(PIDFILE_NAME);
+#else
+    int    temp_len = file_len + sizeof(TEMP_NAME);
+#endif
+    int    total_len = sizeof(FileLock) + file_len + lock_len + temp_len + 3;
+
+    FileLock*  lock = malloc(total_len);
+
+    lock->file = (const char*)(lock + 1);
+    memcpy( (char*)lock->file, file, file_len+1 );
+
+    lock->lock = lock->file + file_len + 1;
+    memcpy( (char*)lock->lock, file, file_len+1 );
+    strcat( (char*)lock->lock, LOCK_NAME );
+
+    lock->temp    = (char*)lock->lock + lock_len + 1;
+#ifdef _WIN32
+    snprintf( (char*)lock->temp, temp_len, "%s\\" PIDFILE_NAME, lock->lock );
+#else
+    lock->temp[0] = 0;
+#endif
+    lock->locked = 0;
+
+    if (filelock_lock(lock) < 0) {
+        free(lock);
+        return NULL;
+    }
+
+    lock->next     = _all_filelocks;
+    _all_filelocks = lock;
+
+    if (lock->next == NULL)
+        atexit( filelock_atexit );
+
+    return lock;
+}
+
+/** PATH HANDLING ROUTINES
+ **
+ **  path_parent() can be used to return the n-level parent of a given directory
+ **  this understands . and .. when encountered in the input path
+ **/
+
+static __inline__ int
+ispathsep(int  c)
+{
+#ifdef _WIN32
+    return (c == '/' || c == '\\');
+#else
+    return (c == '/');
+#endif
+}
+
+char*
+path_parent( const char*  path, int  levels )
+{
+    const char*  end = path + strlen(path);
+    char*        result;
+
+    while (levels > 0) {
+        const char*  base;
+
+        /* trim any trailing path separator */
+        while (end > path && ispathsep(end[-1]))
+            end--;
+
+        base = end;
+        while (base > path && !ispathsep(base[-1]))
+            base--;
+
+        if (base <= path) /* we can't go that far */
+            return NULL;
+
+        if (end == base+1 && base[0] == '.')
+            goto Next;
+
+        if (end == base+2 && base[0] == '.' && base[1] == '.') {
+            levels += 1;
+            goto Next;
+        }
+
+        levels -= 1;
+
+    Next:
+        end = base - 1;
+    }
+    result = malloc( end-path+1 );
+    if (result != NULL) {
+        memcpy( result, path, end-path );
+        result[end-path] = 0;
+    }
+    return result;
+}
+
+static char*
+substring_dup( const char*  start, const char*  end )
+{
+    int    len    = end - start;
+    char*  result = qemu_malloc(len+1);
+    memcpy(result, start, len);
+    result[len] = 0;
+    return result;
+}
+
+int
+path_split( const char*  path, char* *pdirname, char* *pbasename )
+{
+    const char*  end = path + strlen(path);
+    const char*  last;
+    char*        basename;
+
+    /* prepare for errors */
+    if (pdirname)
+        *pdirname = NULL;
+    if (pbasename)
+        *pbasename = NULL;
+
+    /* handle empty path case */
+    if (end == path) {
+        return -1;
+    }
+
+    /* strip trailing path separators */
+    while (end > path && ispathsep(end[-1]))
+        end -= 1;
+
+    /* handle "/" and degenerate cases like "////" */
+    if (end == path) {
+        return -1;
+    }
+
+    /* find last separator */
+    last = end;
+    while (last > path && !ispathsep(last[-1]))
+        last -= 1;
+
+    /* handle cases where there is no path separator */
+    if (last == path) {
+        if (pdirname)
+            *pdirname  = qemu_strdup(".");
+        if (pbasename)
+            *pbasename = substring_dup(path,end);
+        return 0;
+    }
+
+    /* handle "/foo" */
+    if (last == path+1) {
+        if (pdirname)
+            *pdirname  = qemu_strdup("/");
+        if (pbasename)
+            *pbasename = substring_dup(path+1,end);
+        return 0;
+    }
+
+    /* compute basename */
+    basename = substring_dup(last,end);
+    if (strcmp(basename, ".") == 0 || strcmp(basename, "..") == 0) {
+        qemu_free(basename);
+        return -1;
+    }
+
+    if (pbasename)
+        *pbasename = basename;
+    else {
+        qemu_free(basename);
+    }
+
+    /* compute dirname */
+    if (pdirname != NULL)
+        *pdirname = substring_dup(path,last-1);
+
+    return 0;
+}
+
+char*
+path_basename( const char*  path )
+{
+    char*  basename;
+
+    if (path_split(path, NULL, &basename) < 0)
+        return NULL;
+
+    return basename;
+}
+
+char*
+path_dirname( const char*  path )
+{
+    char*  dirname;
+
+    if (path_split(path, &dirname, NULL) < 0)
+        return NULL;
+
+    return dirname;
+}
+
+
+
+
+
+/** MISC FILE AND DIRECTORY HANDLING
+ **/
+
+ABool
+path_exists( const char*  path )
+{
+    int  ret;
+    CHECKED(ret, access(path, F_OK));
+    return (ret == 0) || (errno != ENOENT);
+}
+
+/* checks that a path points to a regular file */
+ABool
+path_is_regular( const char*  path )
+{
+    int          ret;
+    struct stat  st;
+
+    CHECKED(ret, stat(path, &st));
+    if (ret < 0)
+        return 0;
+
+    return S_ISREG(st.st_mode);
+}
+
+
+/* checks that a path points to a directory */
+ABool
+path_is_dir( const char*  path )
+{
+    int          ret;
+    struct stat  st;
+
+    CHECKED(ret, stat(path, &st));
+    if (ret < 0)
+        return 0;
+
+    return S_ISDIR(st.st_mode);
+}
+
+/* checks that one can read/write a given (regular) file */
+ABool
+path_can_read( const char*  path )
+{
+    int  ret;
+    CHECKED(ret, access(path, R_OK));
+    return (ret == 0);
+}
+
+ABool
+path_can_write( const char*  path )
+{
+    int  ret;
+    CHECKED(ret, access(path, R_OK));
+    return (ret == 0);
+}
+
+ABool
+path_can_exec( const char* path )
+{
+    int  ret;
+    CHECKED(ret, access(path, X_OK));
+    return (ret == 0);
+}
+
+/* try to make a directory. returns 0 on success, -1 on failure
+ * (error code in errno) */
+APosixStatus
+path_mkdir( const char*  path, int  mode )
+{
+#ifdef _WIN32
+    (void)mode;
+    return _mkdir(path);
+#else
+    int  ret;
+    CHECKED(ret, mkdir(path, mode));
+    return ret;
+#endif
+}
+
+static APosixStatus
+path_mkdir_recursive( char*  path, unsigned  len, int  mode )
+{
+    char      old_c;
+    int       ret;
+    unsigned  len2;
+
+    /* get rid of trailing separators */
+    while (len > 0 && ispathsep(path[len-1]))
+        len -= 1;
+
+    if (len == 0) {
+        errno = ENOENT;
+        return -1;
+    }
+
+    /* check that the parent exists, 'len2' is the length of
+     * the parent part of the path */
+    len2 = len-1;
+    while (len2 > 0 && !ispathsep(path[len2-1]))
+        len2 -= 1;
+
+    if (len2 > 0) {
+        old_c      = path[len2];
+        path[len2] = 0;
+        ret        = 0;
+        if ( !path_exists(path) ) {
+            /* the parent doesn't exist, so try to create it */
+            ret = path_mkdir_recursive( path, len2, mode );
+        }
+        path[len2] = old_c;
+
+        if (ret < 0)
+            return ret;
+    }
+
+    /* at this point, we now the parent exists */
+    old_c     = path[len];
+    path[len] = 0;
+    ret       = path_mkdir( path, mode );
+    path[len] = old_c;
+
+    return ret;
+}
+
+/* ensure that a given directory exists, create it if not,
+   0 on success, -1 on failure (error code in errno) */
+APosixStatus
+path_mkdir_if_needed( const char*  path, int  mode )
+{
+    int  ret = 0;
+
+    if (!path_exists(path)) {
+        ret = path_mkdir(path, mode);
+
+        if (ret < 0 && errno == ENOENT) {
+            char      temp[MAX_PATH];
+            unsigned  len = (unsigned)strlen(path);
+
+            if (len > sizeof(temp)-1) {
+                errno = EINVAL;
+                return -1;
+            }
+            memcpy( temp, path, len );
+            temp[len] = 0;
+
+            return path_mkdir_recursive(temp, len, mode);
+        }
+    }
+    return ret;
+}
+
+/* return the size of a given file in '*psize'. returns 0 on
+ * success, -1 on failure (error code in errno) */
+APosixStatus
+path_get_size( const char*  path, uint64_t  *psize )
+{
+#ifdef _WIN32
+    /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */
+    /* do not use OpenFile() because it has strange search behaviour that could */
+    /* result in getting the size of a different file */
+    LARGE_INTEGER  size;
+    HANDLE  file = CreateFile( /* lpFilename */        path,
+                               /* dwDesiredAccess */   GENERIC_READ,
+                               /* dwSharedMode */     FILE_SHARE_READ|FILE_SHARE_WRITE,
+                               /* lpSecurityAttributes */  NULL,
+                               /* dwCreationDisposition */ OPEN_EXISTING,
+                               /* dwFlagsAndAttributes */  0,
+                               /* hTemplateFile */      NULL );
+    if (file == INVALID_HANDLE_VALUE) {
+        /* ok, just to play fair */
+        errno = ENOENT;
+        return -1;
+    }
+    if (!GetFileSizeEx(file, &size)) {
+        /* maybe we tried to get the size of a pipe or something like that ? */
+        *psize = 0;
+    }
+    else {
+        *psize = (uint64_t) size.QuadPart;
+    }
+    CloseHandle(file);
+    return 0;
+#else
+    int    ret;
+    struct stat  st;
+
+    CHECKED(ret, stat(path, &st));
+    if (ret == 0) {
+        *psize = (uint64_t) st.st_size;
+    }
+    return ret;
+#endif
+}
+
+/*
+static ABool
+path_is_absolute( const char*  path )
+{
+#ifdef _WIN32
+    if (path == NULL)
+        return 0;
+
+    if (path[0] == '/' || path[0] == '\\')
+        return 1;
+
+*/    /* 'C:' is always considered to be absolute
+     * even if used with a relative path like C:foo which
+     * is different from C:\foo
+     */
+/*    if (path[0] != 0 && path[1] == ':')
+        return 1;
+
+    return 0;
+#else
+    return (path != NULL && path[0] == '/');
+#endif
+}
+*/
+
+/** OTHER FILE UTILITIES
+ **
+ **  path_empty_file() creates an empty file at a given path location.
+ **  if the file already exists, it is truncated without warning
+ **
+ **  path_copy_file() copies one file into another.
+ **
+ **  both functions return 0 on success, and -1 on error
+ **/
+
+APosixStatus
+path_empty_file( const char*  path )
+{
+#ifdef _WIN32
+    int  fd = _creat( path, S_IWRITE );
+#else
+    /* on Unix, only allow the owner to read/write, since the file *
+     * may contain some personal data we don't want to see exposed */
+    int  fd = creat(path, S_IRUSR | S_IWUSR);
+#endif
+    if (fd >= 0) {
+        close(fd);
+        return 0;
+    }
+    return -1;
+}
+
+APosixStatus
+path_copy_file( const char*  dest, const char*  source )
+{
+    int  fd, fs, result = -1;
+
+    /* if the destination doesn't exist, create it */
+    if ( access(source, F_OK)  < 0 ||
+         path_empty_file(dest) < 0) {
+        return -1;
+    }
+
+    if ( access(source, R_OK) < 0 ) {
+        //D("%s: source file is un-readable: %s\n",
+        //  __FUNCTION__, source);
+        return -1;
+    }
+
+#ifdef _WIN32
+    fd = _open(dest, _O_RDWR | _O_BINARY);
+    fs = _open(source, _O_RDONLY |  _O_BINARY);
+#else
+    fd = creat(dest, S_IRUSR | S_IWUSR);
+    fs = open(source, S_IREAD);
+#endif
+    if (fs >= 0 && fd >= 0) {
+        char buf[4096];
+        ssize_t total = 0;
+        ssize_t n;
+        result = 0; /* success */
+        while ((n = read(fs, buf, 4096)) > 0) {
+            if (write(fd, buf, n) != n) {
+                /* write failed. Make it return -1 so that an
+                 * empty file be created. */
+                //D("Failed to copy '%s' to '%s': %s (%d)",
+                //       source, dest, strerror(errno), errno);
+                result = -1;
+                break;
+            }
+            total += n;
+        }
+    }
+
+    if (fs >= 0) {
+        close(fs);
+    }
+    if (fd >= 0) {
+        close(fd);
+    }
+    return result;
+}
+
+
+APosixStatus
+path_delete_file( const char*  path )
+{
+#ifdef _WIN32
+    int  ret = _unlink( path );
+    if (ret == -1 && errno == EACCES) {
+        /* a first call to _unlink will fail if the file is set read-only */
+        /* we can however try to change its mode first and call unlink    */
+        /* again...                                                       */
+        ret = _chmod( path, _S_IREAD | _S_IWRITE );
+        if (ret == 0)
+            ret = _unlink( path );
+    }
+    return ret;
+#else
+    return  unlink(path);
+#endif
+}
+
+
+void*
+path_load_file(const char *fn, size_t  *pSize)
+{
+    char*  data;
+    int    sz;
+    int    fd;
+
+    if (pSize)
+        *pSize = 0;
+
+    data   = NULL;
+
+    fd = open(fn, O_BINARY | O_RDONLY);
+    if(fd < 0) return NULL;
+
+    do {
+        sz = lseek(fd, 0, SEEK_END);
+        if(sz < 0) break;
+
+        if (pSize)
+            *pSize = (size_t) sz;
+
+        if (lseek(fd, 0, SEEK_SET) != 0)
+            break;
+
+        data = "" malloc(sz + 1);
+        if(data == NULL) break;
+
+        if (read(fd, data, sz) != sz)
+            break;
+
+        close(fd);
+        data[sz] = 0;
+
+        return data;
+    } while (0);
+
+    close(fd);
+
+    if(data != NULL)
+        free(data);
+
+    return NULL;
+}
+
+#ifdef _WIN32
+#  define DIR_SEP  ';'
+#else
+#  define DIR_SEP  ':'
+#endif
+
+char*
+path_search_exec( const char* filename )
+{
+    const char* sysPath = getenv("PATH");
+    char        temp[PATH_MAX];
+    const char* p;
+
+    /* If the file contains a directory separator, don't search */
+#ifdef _WIN32
+    if (strchr(filename, '/') != NULL || strchr(filename, '\\') != NULL) {
+#else
+    if (strchr(filename, '/') != NULL) {
+#endif
+        if (path_exists(filename)) {
+            return strdup(filename);
+        } else {
+            return NULL;
+        }
+    }
+
+    /* If system path is empty, don't search */
+    if (sysPath == NULL || sysPath[0] == '\0') {
+        return NULL;
+    }
+
+    /* Count the number of non-empty items in the system path
+     * Items are separated by DIR_SEP, and two successive separators
+     * correspond to an empty item that will be ignored.
+     * Also compute the required string storage length. */
+    p       = sysPath;
+
+    while (*p) {
+        char* p2 = strchr(p, DIR_SEP);
+        int   len;
+        if (p2 == NULL) {
+            len = strlen(p);
+        } else {
+            len = p2 - p;
+        }
+
+        do {
+            if (len <= 0)
+                break;
+
+            snprintf(temp, sizeof(temp), "%.*s/%s", len, p, filename);
+
+            if (path_exists(temp) && path_can_exec(temp)) {
+                return strdup(temp);
+            }
+
+        } while (0);
+
+        p += len;
+        if (*p == DIR_SEP)
+            p++;
+    }
+
+    /* Nothing, really */
+    return NULL;
+}
diff --git a/hw/goldfish_device.h b/hw/goldfish_device.h
index 4a123e5..91d74fd 100644
--- a/hw/goldfish_device.h
+++ b/hw/goldfish_device.h
@@ -46,6 +46,7 @@ DeviceState *goldfish_int_create(GoldfishBus *gbus, uint32_t base, qemu_irq pare
 DeviceState *goldfish_timer_create(GoldfishBus *gbus, uint32_t base, int irq);
 DeviceState *goldfish_rtc_create(GoldfishBus *gbus);
 DeviceState *goldfish_tty_create(GoldfishBus *gbus, CharDriverState *cs, int id, uint32_t base, int irq);
+DeviceState *goldfish_nand_create(GoldfishBus *gbus);
 
 /* Global functions provided by Goldfish devices */
 void goldfish_bus_register_withprop(GoldfishDeviceInfo *info);
@@ -53,4 +54,208 @@ int goldfish_add_device_no_io(GoldfishDevice *dev);
 void goldfish_device_init(DeviceState *dev, uint32_t base, uint32_t irq);
 void goldfish_device_set_irq(GoldfishDevice *dev, int irq, int level);
 
+/** TEMP FILE SUPPORT
+ **
+ ** simple interface to create an empty temporary file on the system.
+ **
+ ** create the file with tempfile_create(), which returns a reference to a TempFile
+ ** object, or NULL if your system is so weird it doesn't have a temporary directory.
+ **
+ ** you can then call tempfile_path() to retrieve the TempFile's real path to open
+ ** it. the returned path is owned by the TempFile object and should not be freed.
+ **
+ ** all temporary files are destroyed when the program quits, unless you explicitely
+ ** close them before that with tempfile_close()
+ **/
+
+typedef struct TempFile   TempFile;
+
+extern  TempFile*    tempfile_create( void );
+extern  const char*  tempfile_path( TempFile*  temp );
+extern  void         tempfile_close( TempFile*  temp );
+
+/** TEMP FILE CLEANUP
+ **
+ ** We delete all temporary files in atexit()-registered callbacks.
+ ** however, the Win32 DeleteFile is unable to remove a file unless
+ ** all HANDLEs to it are closed in the terminating process.
+ **
+ ** Call 'atexit_close_fd' on a newly open-ed file descriptor to indicate
+ ** that you want it closed in atexit() time. You should always call
+ ** this function unless you're certain that the corresponding file
+ ** cannot be temporary.
+ **
+ ** Call 'atexit_close_fd_remove' before explicitely closing a 'fd'
+ **/
+extern void          atexit_close_fd(int  fd);
+extern void          atexit_close_fd_remove(int  fd);
+
+/** MISC FILE AND DIRECTORY HANDLING
+ **/
+
+/* O_BINARY is required in the MS C library to avoid opening file
+ * in text mode (the default, ahhhhh)
+ */
+#if !defined(_WIN32) && !defined(O_BINARY)
+#  define  O_BINARY  0
+#endif
+
+/* define  PATH_SEP as a string containing the directory separateor */
+#ifdef _WIN32
+#  define  PATH_SEP   "\\"
+#  define  PATH_SEP_C '\\'
+#else
+#  define  PATH_SEP   "/"
+#  define  PATH_SEP_C '/'
+#endif
+
+/* get MAX_PATH, note that PATH_MAX is set to 260 on Windows for
+ * stupid backwards-compatibility reason, though any 32-bit version
+ * of the OS handles much much longer paths
+ */
+#ifdef _WIN32
+#  undef   MAX_PATH
+#  define  MAX_PATH    1024
+#  undef   PATH_MAX
+#  define  PATH_MAX    MAX_PATH
+#else
+#  include <limits.h>
+#  define  MAX_PATH    PATH_MAX
+#endif
+
+typedef int ABool;
+typedef int APosixStatus;
+
+/* checks that a given file exists */
+extern ABool  path_exists( const char*  path );
+
+/* checks that a path points to a regular file */
+extern ABool  path_is_regular( const char*  path );
+
+/* checks that a path points to a directory */
+extern ABool  path_is_dir( const char*  path );
+
+/* checks that a path is absolute or not */
+//extern ABool  path_is_absolute( const char*  path );
+
+/* checks that one can read/write a given (regular) file */
+extern ABool  path_can_read( const char*  path );
+extern ABool  path_can_write( const char*  path );
+
+/* checks that one can execute a given file */
+extern ABool  path_can_exec( const char* path );
+
+/* try to make a directory */
+extern APosixStatus   path_mkdir( const char*  path, int  mode );
+
+/* ensure that a given directory exists, create it if not,
+   0 on success, -1 on error */
+extern APosixStatus   path_mkdir_if_needed( const char*  path, int  mode );
+
+/* return the size of a given file in '*psize'. returns 0 on
+ * success, -1 on failure (error code in errno) */
+extern APosixStatus   path_get_size( const char*  path, uint64_t  *psize );
+
+/*  path_parent() can be used to return the n-level parent of a given directory
+ *  this understands . and .. when encountered in the input path.
+ *
+ *  the returned string must be freed by the caller.
+ */
+extern char*  path_parent( const char*  path, int  levels );
+
+/* split a path into a (dirname,basename) pair. the result strings must be freed
+ * by the caller. Return 0 on success, or -1 on error. Error conditions include
+ * the following:
+ *   - 'path' is empty
+ *   - 'path' is "/" or degenerate cases like "////"
+ *   - basename is "." or ".."
+ *
+ * if there is no directory separator in path, *dirname will be set to "."
+ * if the path is of type "/foo", then *dirname will be set to "/"
+ *
+ * pdirname can be NULL if you don't want the directory name
+ * pbasename can be NULL if you don't want the base name
+ */
+extern int    path_split( const char*  path, char* *pdirname, char* *pbasename );
+
+/* a convenience function to retrieve the directory name as returned by
+ * path_split(). Returns NULL if path_split() returns an error.
+ * the result string must be freed by the caller
+ */
+extern char*  path_dirname( const char*  path );
+
+/* a convenience function to retrieve the base name as returned by
+ * path_split(). Returns NULL if path_split() returns an error.
+ * the result must be freed by the caller.
+ */
+extern char*  path_basename( const char*  path );
+
+/* look for a given executable in the system path and return its full path.
+ * Returns NULL if not found. Note that on Windows this doesn't not append
+ * an .exe prefix, or other magical thing like Cygwin usually does.
+ */
+extern char*  path_search_exec( const char* filename );
+
+/** OTHER FILE UTILITIES
+ **
+ **  path_empty_file() creates an empty file at a given path location.
+ **  if the file already exists, it is truncated without warning
+ **
+ **  path_copy_file() copies one file into another.
+ **
+ **  unlink_file() is equivalent to unlink() on Unix, on Windows,
+ **  it will handle the case where _unlink() fails because the file is
+ **  read-only by trying to change its access rights then calling _unlink()
+ **  again.
+ **
+ **  these functions return 0 on success, and -1 on error
+ **
+ **  load_text_file() reads a file into a heap-allocated memory block,
+ **  and appends a 0 to it. the caller must free it
+ **/
+
+/* creates an empty file at a given location. If the file already
+ * exists, it is truncated without warning. returns 0 on success,
+ * or -1 on failure.
+ */
+extern APosixStatus   path_empty_file( const char*  path );
+
+/* copies on file into another one. 0 on success, -1 on failure
+ * (error code in errno). Does not work on directories */
+extern APosixStatus   path_copy_file( const char*  dest, const char*  source );
+
+/* unlink/delete a given file. Note that on Win32, this will
+ * fail if the program has an opened handle to the file
+ */
+extern APosixStatus   path_delete_file( const char*  path );
+
+/* try to load a given file into a heap-allocated block.
+ * if 'pSize' is not NULL, this will set the file's size in '*pSize'
+ * note that this actually zero-terminates the file for convenience.
+ * In case of failure, NULL is returned and the error code is in errno
+ */
+extern void*          path_load_file( const char*  path, size_t  *pSize );
+
+/** FILE LOCKS SUPPORT
+ **
+ ** a FileLock is useful to prevent several emulator instances from using the same
+ ** writable file (e.g. the userdata.img disk images).
+ **
+ ** create a FileLock object with filelock_create(), the function will return
+ ** NULL only if the corresponding path is already locked by another emulator
+ ** of if the path is read-only.
+ **
+ ** note that 'path' can designate a non-existing path and that the lock creation
+ ** function can detect stale file locks that can longer when the emulator
+ ** crashes unexpectedly, and will happily clean them for you.
+ **
+ ** you can call filelock_release() to release a file lock explicitely. otherwise
+ ** all file locks are automatically released when the program exits.
+ **/
+
+typedef struct FileLock  FileLock;
+
+extern FileLock*  filelock_create ( const char*  path );
+extern void       filelock_release( FileLock*  lock );
+
 #endif
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c
new file mode 100644
index 0000000..f540bda
--- /dev/null
+++ b/hw/goldfish_nand.c
@@ -0,0 +1,914 @@
+/* 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 "hw.h"
+#include "goldfish_device.h"
+#include "goldfish_nand_reg.h"
+#include "goldfish_nand.h"
+
+#ifdef TARGET_I386
+#include "kvm.h"
+#endif
+
+#define  DEBUG  0
+#if DEBUG
+#  define  D(...)    VERBOSE_PRINT(init,__VA_ARGS__)
+#  define  D_ACTIVE  VERBOSE_CHECK(init)
+#  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
+#define  PANIC(...) do { fprintf(stderr, __VA_ARGS__);  \
+                         exit(1);                       \
+                    } while (0)
+
+/* 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);
+    */
+}
+
+/* Information on a single device/nand image used by the emulator
+ */
+typedef struct {
+    char*      devname;      /* name for this device (not null-terminated, use len below) */
+    size_t     devname_len;
+    uint8_t*   data;         /* buffer for read/write actions to underlying image */
+    int        fd;
+    uint32_t   flags;
+    uint32_t   page_size;
+    uint32_t   extra_size;
+    uint32_t   erase_size;   /* size of the data buffer mentioned above */
+    uint64_t   max_size;     /* Capacity limit for the image. The actual underlying
+                              * file may be smaller. */
+} 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;
+
+/* The controller is the single access point for all NAND images currently
+ * attached to the system.
+ */
+typedef struct {
+    uint32_t base;
+
+    // register state
+    uint32_t dev;            /* offset in nand_devs for the device that is
+                              * currently being accessed */
+    uint32_t addr_low;
+    uint32_t addr_high;
+    uint32_t transfer_size;
+    uint32_t data;
+    uint32_t result;
+} nand_dev_controller_state;
+
+typedef struct GoldfishNandDevice {
+    GoldfishDevice qdev;
+    char *system_path;
+    char *system_init_path;
+    uint64_t system_size;
+
+    char *user_data_path;
+    char *user_data_init_path;
+    uint64_t user_data_size;
+    
+    char *cache_path;
+    uint64_t cache_size;
+    uint32_t base;
+
+    // register state
+    uint32_t dev;            /* offset in nand_devs for the device that is
+                              * currently being accessed */
+    uint32_t addr_low;
+    uint32_t addr_high;
+    uint32_t transfer_size;
+    uint32_t data;
+    uint32_t result;
+} GoldfishNandDevice;
+
+/* EINTR-proof read - due to SIGALRM in use elsewhere */
+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;
+}
+
+/* EINTR-proof write - due to SIGALRM in use elsewhere */
+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;
+}
+
+/* EINTR-proof lseek - due to SIGALRM in use elsewhere */
+static int  do_lseek(int  fd, off_t offset, int whence)
+{
+    int  ret;
+    do {
+        ret = lseek(fd, offset, whence);
+    } 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);
+
+    do_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);
+        }
+#ifdef TARGET_I386
+        if (kvm_enabled())
+            cpu_synchronize_state(cpu_single_env, 0);
+#endif
+        cpu_memory_rw_debug(cpu_single_env, data, dev->data, read_len, 1);
+        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);
+
+    do_lseek(dev->fd, addr, SEEK_SET);
+    while(len > 0) {
+        if(len < write_len)
+            write_len = len;
+#ifdef TARGET_I386
+        if (kvm_enabled())
+                cpu_synchronize_state(cpu_single_env, 0);
+#endif
+        cpu_memory_rw_debug(cpu_single_env, data, dev->data, write_len, 0);
+        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;
+
+    do_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_controller_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;
+#ifdef TARGET_I386
+        if (kvm_enabled())
+                cpu_synchronize_state(cpu_single_env, 0);
+#endif
+        cpu_memory_rw_debug(cpu_single_env, s->data, (uint8_t*)dev->devname, size, 1);
+        return size;
+    case NAND_CMD_READ:
+        if(addr >= dev->max_size)
+            return 0;
+        if(size > dev->max_size - addr)
+            size = dev->max_size - addr;
+        if(dev->fd >= 0)
+            return nand_dev_read_file(dev, s->data, addr, size);
+#ifdef TARGET_I386
+        if (kvm_enabled())
+                cpu_synchronize_state(cpu_single_env, 0);
+#endif
+        cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 1);
+        return size;
+    case NAND_CMD_WRITE:
+        if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+            return 0;
+        if(addr >= dev->max_size)
+            return 0;
+        if(size > dev->max_size - addr)
+            size = dev->max_size - addr;
+        if(dev->fd >= 0)
+            return nand_dev_write_file(dev, s->data, addr, size);
+#ifdef TARGET_I386
+        if (kvm_enabled())
+                cpu_synchronize_state(cpu_single_env, 0);
+#endif
+        cpu_memory_rw_debug(cpu_single_env,s->data, &dev->data[addr], size, 0);
+        return size;
+    case NAND_CMD_ERASE:
+        if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+            return 0;
+        if(addr >= dev->max_size)
+            return 0;
+        if(size > dev->max_size - addr)
+            size = dev->max_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_controller_state *s = (nand_dev_controller_state *)opaque;
+
+    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 = "">
+        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_controller_state *s = (nand_dev_controller_state *)opaque;
+    nand_dev *dev;
+
+    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->max_size;
+
+    case NAND_DEV_SIZE_HIGH:
+        return (uint32_t)(dev->max_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 */
+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;
+
+    //VERBOSE_PRINT(init, "%s: %s", __FUNCTION__, arg);
+
+    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+1);
+            if(devname == NULL)
+                goto out_of_memory;
+            memcpy(devname, arg, arg_len);
+            devname[arg_len] = 0;
+        }
+        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\n",
+                  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) {
+            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 = do_lseek(initfd, 0, SEEK_END);
+            do_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);
+        D("rounding devsize up to a full eraseunit, now %llx\n", dev_size);
+    }
+    dev->devname = devname;
+    dev->devname_len = devname_len;
+    dev->max_size = dev_size;
+    dev->data = "">
+    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", rwfilename, 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);
+}
+
+static int goldfish_nand_init(GoldfishDevice *dev)
+{
+    GoldfishNandDevice *s = (GoldfishNandDevice *)dev;
+    /* Initialize system partition image */
+    {
+        char        tmp[PATH_MAX+32];
+        const char* sysImage = s->system_path;
+        const char* initImage = s->system_init_path;
+        uint64_t    sysBytes = s->system_size;
+
+        if (sysBytes == 0) {
+            PANIC("Invalid system partition size: %jd", sysBytes);
+        }
+
+        snprintf(tmp,sizeof(tmp),"system,size=0x%jx", sysBytes);
+
+        if (sysImage && *sysImage) {
+            if (filelock_create(sysImage) == NULL) {
+                fprintf(stderr,"WARNING: System image already in use, changes will not persist!\n");
+                /* If there is no file= parameters, nand_add_dev will create
+                 * a temporary file to back the partition image. */
+            } else {
+                pstrcat(tmp,sizeof(tmp),",file=");
+                pstrcat(tmp,sizeof(tmp),sysImage);
+            }
+        }
+        if (initImage && *initImage) {
+            if (!path_exists(initImage)) {
+                PANIC("Invalid initial system image path: %s", initImage);
+            }
+            pstrcat(tmp,sizeof(tmp),",initfile=");
+            pstrcat(tmp,sizeof(tmp),initImage);
+        } else {
+            PANIC("Missing initial system image path!");
+        }
+        nand_add_dev(tmp);
+    }
+
+    /* Initialize data partition image */
+    {
+        char        tmp[PATH_MAX+32];
+        const char* dataImage = s->user_data_path;
+        const char* initImage = s->user_data_init_path;
+        uint64_t    dataBytes = s->user_data_size;
+
+        if (dataBytes == 0) {
+            PANIC("Invalid data partition size: %jd", dataBytes);
+        }
+
+        snprintf(tmp,sizeof(tmp),"userdata,size=0x%jx", dataBytes);
+
+        if (dataImage && *dataImage) {
+            if (filelock_create(dataImage) == NULL) {
+                fprintf(stderr, "WARNING: Data partition already in use. Changes will not persist!\n");
+                /* Note: if there is no file= parameters, nand_add_dev() will
+                 *       create a temporary file to back the partition image. */
+            } else {
+                /* Create the file if needed */
+                if (!path_exists(dataImage)) {
+                    if (path_empty_file(dataImage) < 0) {
+                        PANIC("Could not create data image file %s: %s", dataImage, strerror(errno));
+                    }
+                }
+                pstrcat(tmp, sizeof(tmp), ",file=");
+                pstrcat(tmp, sizeof(tmp), dataImage);
+            }
+        }
+        if (initImage && *initImage) {
+            pstrcat(tmp, sizeof(tmp), ",initfile=");
+            pstrcat(tmp, sizeof(tmp), initImage);
+        }
+        nand_add_dev(tmp);
+    }
+
+    /* Initialize cache partition */
+    {
+        char        tmp[PATH_MAX+32];
+        const char* partPath = s->cache_path;
+        uint64_t    partSize = s->cache_size;
+
+        snprintf(tmp,sizeof(tmp),"cache,size=0x%jx", partSize);
+
+        if (partPath && *partPath && strcmp(partPath, "<temp>") != 0) {
+            if (filelock_create(partPath) == NULL) {
+                fprintf(stderr, "WARNING: Cache partition already in use. Changes will not persist!\n");
+                /* Note: if there is no file= parameters, nand_add_dev() will
+                 *       create a temporary file to back the partition image. */
+            } else {
+                /* Create the file if needed */
+                if (!path_exists(partPath)) {
+                    if (path_empty_file(partPath) < 0) {
+                        PANIC("Could not create cache image file %s: %s", partPath, strerror(errno));
+                    }
+                }
+                pstrcat(tmp, sizeof(tmp), ",file=");
+                pstrcat(tmp, sizeof(tmp), partPath);
+            }
+        }
+        nand_add_dev(tmp);
+    }
+    return 0;
+}
+
+DeviceState *goldfish_nand_create(GoldfishBus *gbus)
+{
+    DeviceState *dev;
+    char *name = (char *)"goldfish_nand";
+
+    dev = qdev_create(&gbus->bus, name);
+    qdev_prop_set_string(dev, "name", name);
+    qdev_init_nofail(dev);
+
+    return dev;
+}
+
+static GoldfishDeviceInfo goldfish_nand_info = {
+    .init = goldfish_nand_init,
+    .readfn = nand_dev_readfn,
+    .writefn = nand_dev_writefn,
+    .qdev.name  = "goldfish_nand",
+    .qdev.size  = sizeof(GoldfishNandDevice),
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("base", GoldfishDevice, base, 0),
+        DEFINE_PROP_UINT32("id", GoldfishDevice, id, 0),
+        DEFINE_PROP_UINT32("size", GoldfishDevice, size, 0xfff),
+        DEFINE_PROP_UINT32("irq", GoldfishDevice, irq, 0),
+        DEFINE_PROP_UINT32("irq_count", GoldfishDevice, irq_count, 1),
+        DEFINE_PROP_STRING("name", GoldfishDevice, name),
+        DEFINE_PROP_STRING("system_path", GoldfishNandDevice, system_path),
+        DEFINE_PROP_STRING("system_init_path", GoldfishNandDevice, system_init_path),
+        DEFINE_PROP_UINT64("system_size", GoldfishNandDevice, system_size, 0x7100000),
+        DEFINE_PROP_STRING("user_data_path", GoldfishNandDevice, user_data_path),
+        DEFINE_PROP_STRING("user_data_init_path", GoldfishNandDevice, user_data_init_path),
+        DEFINE_PROP_UINT64("user_data_size", GoldfishNandDevice, user_data_size, 0x4200000),
+        DEFINE_PROP_STRING("cache_path", GoldfishNandDevice, cache_path),
+        DEFINE_PROP_UINT64("cache_size", GoldfishNandDevice, cache_size, 0x4200000),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void goldfish_nand_register(void)
+{
+    goldfish_bus_register_withprop(&goldfish_nand_info);
+}
+device_init(goldfish_nand_register);
+
+#ifdef CONFIG_NAND_LIMITS
+
+static uint64_t
+parse_nand_rw_limit( const char*  value )
+{
+    char*     end;
+    uint64_t  val = strtoul( value, &end, 0 );
+
+    if (end == value) {
+        derror( "bad parameter value '%s': expecting unsigned integer", value );
+        exit(1);
+    }
+
+    switch (end[0]) {
+        case 'K':  val <<= 10; break;
+        case 'M':  val <<= 20; break;
+        case 'G':  val <<= 30; break;
+        case 0: break;
+        default:
+            derror( "bad read/write limit suffix: use K, M or G" );
+            exit(1);
+    }
+    return val;
+}
+
+void
+parse_nand_limits(char*  limits)
+{
+    int      pid = -1, signal = -1;
+    int64_t  reads = 0, writes = 0;
+    char*    item = limits;
+
+    /* parse over comma-separated items */
+    while (item && *item) {
+        char*  next = strchr(item, ',');
+        char*  end;
+
+        if (next == NULL) {
+            next = item + strlen(item);
+        } else {
+            *next++ = 0;
+        }
+
+        if ( !memcmp(item, "pid=", 4) ) {
+            pid = strtol(item+4, &end, 10);
+            if (end == NULL || *end) {
+                derror( "bad parameter, expecting pid=<number>, got '%s'",
+                        item );
+                exit(1);
+            }
+            if (pid <= 0) {
+                derror( "bad parameter: process identifier must be > 0" );
+                exit(1);
+            }
+        }
+        else if ( !memcmp(item, "signal=", 7) ) {
+            signal = strtol(item+7,&end, 10);
+            if (end == NULL || *end) {
+                derror( "bad parameter: expecting signal=<number>, got '%s'",
+                        item );
+                exit(1);
+            }
+            if (signal <= 0) {
+                derror( "bad parameter: signal number must be > 0" );
+                exit(1);
+            }
+        }
+        else if ( !memcmp(item, "reads=", 6) ) {
+            reads = parse_nand_rw_limit(item+6);
+        }
+        else if ( !memcmp(item, "writes=", 7) ) {
+            writes = parse_nand_rw_limit(item+7);
+        }
+        else {
+            derror( "bad parameter '%s' (see -help-nand-limits)", item );
+            exit(1);
+        }
+        item = next;
+    }
+    if (pid < 0) {
+        derror( "bad paramater: missing pid=<number>" );
+        exit(1);
+    }
+    else if (signal < 0) {
+        derror( "bad parameter: missing signal=<number>" );
+        exit(1);
+    }
+    else if (reads == 0 && writes == 0) {
+        dwarning( "no read or write limit specified. ignoring -nand-limits" );
+    } else {
+        nand_threshold*  t;
+
+        t  = &android_nand_read_threshold;
+        t->pid     = pid;
+        t->signal  = signal;
+        t->counter = 0;
+        t->limit   = reads;
+
+        t  = &android_nand_write_threshold;
+        t->pid     = pid;
+        t->signal  = signal;
+        t->counter = 0;
+        t->limit   = writes;
+    }
+}
+#endif /* CONFIG_NAND_LIMITS */
diff --git a/hw/goldfish_nand.h b/hw/goldfish_nand.h
new file mode 100644
index 0000000..a8f3652
--- /dev/null
+++ b/hw/goldfish_nand.h
@@ -0,0 +1,29 @@
+/* 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.
+*/
+#ifndef NAND_DEVICE_H
+#define NAND_DEVICE_H
+
+void nand_dev_init(uint32_t base);
+void nand_add_dev(const char *arg);
+void parse_nand_limits(char*  limits);
+
+typedef struct {
+    uint64_t     limit;
+    uint64_t     counter;
+    int          pid;
+    int          signal;
+} nand_threshold;
+
+extern nand_threshold   android_nand_read_threshold;
+extern nand_threshold   android_nand_write_threshold;
+
+#endif
diff --git a/hw/goldfish_nand_reg.h b/hw/goldfish_nand_reg.h
new file mode 100644
index 0000000..ea91461
--- /dev/null
+++ b/hw/goldfish_nand_reg.h
@@ -0,0 +1,54 @@
+/* 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.
+*/
+#ifndef NAND_DEVICE_REG_H
+#define NAND_DEVICE_REG_H
+
+enum nand_cmd {
+ NAND_CMD_GET_DEV_NAME,  // Write device name for NAND_DEV to NAND_DATA (vaddr)
+ NAND_CMD_READ,
+ NAND_CMD_WRITE,
+ NAND_CMD_ERASE,
+ NAND_CMD_BLOCK_BAD_GET, // NAND_RESULT is 1 if block is bad, 0 if it is not
+ NAND_CMD_BLOCK_BAD_SET
+};
+
+enum nand_dev_flags {
+ NAND_DEV_FLAG_READ_ONLY = 0x00000001
+};
+
+#define NAND_VERSION_CURRENT (1)
+
+enum nand_reg {
+ // Global
+ NAND_VERSION        = 0x000,
+ NAND_NUM_DEV        = 0x004,
+ NAND_DEV            = 0x008,
+
+ // Dev info
+ NAND_DEV_FLAGS      = 0x010,
+ NAND_DEV_NAME_LEN   = 0x014,
+ NAND_DEV_PAGE_SIZE  = 0x018,
+ NAND_DEV_EXTRA_SIZE = 0x01c,
+ NAND_DEV_ERASE_SIZE = 0x020,
+ NAND_DEV_SIZE_LOW   = 0x028,
+ NAND_DEV_SIZE_HIGH  = 0x02c,
+
+ // Command
+ NAND_RESULT         = 0x040,
+ NAND_COMMAND        = 0x044,
+ NAND_DATA           = 0x048,
+ NAND_TRANSFER_SIZE  = 0x04c,
+ NAND_ADDR_LOW       = 0x050,
+ NAND_ADDR_HIGH      = 0x054,
+};
+
+#endif
-- 
1.7.4.1


reply via email to

[Prev in Thread] Current Thread [Next in Thread]