All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller.
@ 2011-08-22  9:39 Patrick Jackson
  2011-08-22 12:04 ` Stefan Hajnoczi
  0 siblings, 1 reply; 5+ messages in thread
From: Patrick Jackson @ 2011-08-22  9:39 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 77188 bytes --]

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 <PatrickSJackson@gmail.com>
---
 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 = (char*) 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 = 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_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 = 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", 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

[-- Attachment #2: Type: text/html, Size: 111527 bytes --]

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller.
  2011-08-22  9:39 [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller Patrick Jackson
@ 2011-08-22 12:04 ` Stefan Hajnoczi
  2011-08-22 14:16   ` Paolo Bonzini
  0 siblings, 1 reply; 5+ messages in thread
From: Stefan Hajnoczi @ 2011-08-22 12:04 UTC (permalink / raw)
  To: Patrick Jackson; +Cc: qemu-devel

On Mon, Aug 22, 2011 at 10:39 AM, Patrick Jackson
<patricksjackson@gmail.com> wrote:
>
> 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 <PatrickSJackson@gmail.com>
> ---
>  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

Please use the block layer instead of reinventing portable file I/O.

Also, please check which of the utility functions already exist in
QEMU.  If a utility function in this patch is needed it should be made
generic for all of QEMU, not just goldfish or nand.

Stefan

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller.
  2011-08-22 12:04 ` Stefan Hajnoczi
@ 2011-08-22 14:16   ` Paolo Bonzini
  2011-08-22 20:41     ` Stefan Hajnoczi
  0 siblings, 1 reply; 5+ messages in thread
From: Paolo Bonzini @ 2011-08-22 14:16 UTC (permalink / raw)
  To: Stefan Hajnoczi; +Cc: qemu-devel, Patrick Jackson

On 08/22/2011 02:04 PM, Stefan Hajnoczi wrote:
> Please use the block layer instead of reinventing portable file I/O.
>
> Also, please check which of the utility functions already exist in
> QEMU.  If a utility function in this patch is needed it should be made
> generic for all of QEMU, not just goldfish or nand.

This was part of the plans, but there was no time for it.

Paolo

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller.
  2011-08-22 14:16   ` Paolo Bonzini
@ 2011-08-22 20:41     ` Stefan Hajnoczi
  2011-08-23  7:17       ` Paolo Bonzini
  0 siblings, 1 reply; 5+ messages in thread
From: Stefan Hajnoczi @ 2011-08-22 20:41 UTC (permalink / raw)
  To: Patrick Jackson; +Cc: Paolo Bonzini, qemu-devel

On Mon, Aug 22, 2011 at 3:16 PM, Paolo Bonzini <pbonzini@redhat.com> wrote:
> On 08/22/2011 02:04 PM, Stefan Hajnoczi wrote:
>>
>> Please use the block layer instead of reinventing portable file I/O.
>>
>> Also, please check which of the utility functions already exist in
>> QEMU.  If a utility function in this patch is needed it should be made
>> generic for all of QEMU, not just goldfish or nand.
>
> This was part of the plans, but there was no time for it.

Does that mean this series is Request For Comments and not something
you expect to be merged into qemu.git?

Stefan

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller.
  2011-08-22 20:41     ` Stefan Hajnoczi
@ 2011-08-23  7:17       ` Paolo Bonzini
  0 siblings, 0 replies; 5+ messages in thread
From: Paolo Bonzini @ 2011-08-23  7:17 UTC (permalink / raw)
  To: Stefan Hajnoczi; +Cc: qemu-devel, Patrick Jackson

On 08/22/2011 10:41 PM, Stefan Hajnoczi wrote:
> On Mon, Aug 22, 2011 at 3:16 PM, Paolo Bonzini<pbonzini@redhat.com>  wrote:
>> On 08/22/2011 02:04 PM, Stefan Hajnoczi wrote:
>>>
>>> Please use the block layer instead of reinventing portable file I/O.
>>>
>>> Also, please check which of the utility functions already exist in
>>> QEMU.  If a utility function in this patch is needed it should be made
>>> generic for all of QEMU, not just goldfish or nand.
>>
>> This was part of the plans, but there was no time for it.
>
> Does that mean this series is Request For Comments and not something
> you expect to be merged into qemu.git?

Yes, more or less.

Paolo

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-08-23  7:17 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-22  9:39 [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller Patrick Jackson
2011-08-22 12:04 ` Stefan Hajnoczi
2011-08-22 14:16   ` Paolo Bonzini
2011-08-22 20:41     ` Stefan Hajnoczi
2011-08-23  7:17       ` Paolo Bonzini

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.