From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:56471) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QvR6A-0000BI-1u for qemu-devel@nongnu.org; Mon, 22 Aug 2011 05:47:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QvR60-0008GZ-4t for qemu-devel@nongnu.org; Mon, 22 Aug 2011 05:47:14 -0400 Received: from mail-qw0-f45.google.com ([209.85.216.45]:41992) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QvR5z-0008GT-KJ for qemu-devel@nongnu.org; Mon, 22 Aug 2011 05:47:04 -0400 Received: by qwj8 with SMTP id 8so3555834qwj.4 for ; Mon, 22 Aug 2011 02:47:03 -0700 (PDT) MIME-Version: 1.0 Date: Mon, 22 Aug 2011 05:39:04 -0400 Message-ID: From: Patrick Jackson Content-Type: multipart/alternative; boundary=00163649982d92628004ab14d90a Subject: [Qemu-devel] [PATCH 06/11] Goldfish: Added a nand controller. List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --00163649982d92628004ab14d90a Content-Type: text/plain; charset=ISO-8859-1 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 --- 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 +# include +#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 +#include + +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 +# include +# include +#else +# include +# include +# include +#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 +# 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, "") != 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=, 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=, 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=" ); + exit(1); + } + else if (signal < 0) { + derror( "bad parameter: missing signal=" ); + 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 --00163649982d92628004ab14d90a Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable

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

-global goldfish_nand.= system_init_path=3D/path/to/initial/system/image -global goldfish_nand.user= _data_path=3D/path/to/user/data

Signed-off-by: Patrick Jackson <PatrickSJackson@gmail.com>= ;
---
=A0Makefile.target =A0 =A0 =A0 =A0| =A0 =A02 +-
=A0hw/android_arm.c =A0 =A0 =A0 | =A0 =A02 +
=A0hw/goldfish_device.c =A0 | 1398 +++++++++++++++++++++++++++++++++++= +++++++++++++
=A0hw/goldfish_device.h =A0 | =A0205 +++++++
<= div>=A0hw/goldfish_nand.c =A0 =A0 | =A0914 +++++++++++++++++++++++++++++++<= /div>
=A0hw/goldfish_nand.h =A0 =A0 | =A0 29 +
=A0hw/goldfish_nand_reg.h | =A0 54 ++
=A07 files changed, 26= 03 insertions(+), 1 deletions(-)
=A0create mode 100644 hw/goldfis= h_nand.c
=A0create mode 100644 hw/goldfish_nand.h
=A0cr= eate 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 +=3D vexpress.= o
=A0obj-arm-y +=3D strongarm.o
=A0obj-arm-y +=3D collie.o
=A0obj-arm-y +=3D android_arm.o goldfish_device.o goldfish_interrupt= .o goldfish_timer.o
-obj-arm-y +=3D goldfish_tty.o
+obj= -arm-y +=3D goldfish_tty.o goldfish_nand.o
=A0
=A0obj-sh4-y =3D shix.o r2d.o sh7750.o sh7750_regnames.o= tc58128.o
=A0obj-sh4-y +=3D 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_in= it_(ram_addr_t ram_size,
=A0 =A0 =A0 =A0 =A0}
=A0 =A0 = =A0}
=A0
+ =A0 =A0goldfish_nand_create(gbus);
+
= =A0 =A0 =A0info.ram_size =A0 =A0 =A0 =A0=3D ram_size;
=A0 =A0 =A0= info.kernel_filename =3D kernel_filename;
=A0 =A0 =A0info.kernel_= cmdline =A0=3D kernel_cmdline;
diff --git a/hw/goldfish_device.c b/hw/goldfish_device.c
index 39= f5247..b34ee92 100644
--- a/hw/goldfish_device.c
+++ b/= hw/goldfish_device.c
@@ -292,3 +292,1401 @@ static void goldfish_= register_devices(void)
=A0
=A0device_init(goldfish_register_devices)
=A0<= /div>
+#define =A0D(...) =A0((void)0)
+
+#ifdef _WI= N32
+# =A0define WIN32_LEAN_AND_MEAN
+# =A0include &quo= t;windows.h"
+# =A0include "shlobj.h"
+#else
+# =A0in= clude <unistd.h>
+# =A0include <sys/stat.h>
+#endif
+
+/** FORMATTED BUFFER PRINTING
+ *= *
+ ** =A0bufprint() allows your to easily and safely append formatted s= tring
+ ** =A0content to a given bounded character buffer, in a w= ay that is easier
+ ** =A0to use than raw snprintf()
+ = **
+ ** =A0'buffer' =A0is the start position in the buffer,
=
+ ** =A0'buffend' is the end of the buffer, the function assum= es (buffer <=3D buffend)
+ ** =A0'format' =A0is a stan= dard printf-style format string, followed by any number
+ ** =A0 =A0 =A0 =A0 =A0 =A0of formatting arguments
+ **
+ ** =A0the function returns the next position in the buffer if ever= ything fits
+ ** =A0in it. in case of overflow or formatting erro= r, it will always return "buffend"
+ **
+ ** =A0this allows you to chain several calls to bufpr= int() and only check for
+ ** =A0overflow at the end, for exemple= :
+ **
+ ** =A0 =A0 char =A0 buffer[1024];
+ = ** =A0 =A0 char* =A0p =A0 =3D buffer;
+ ** =A0 =A0 char* =A0end =3D p + sizeof(buffer);
+ **
=
+ ** =A0 =A0 p =3D bufprint(p, end, "%s/%s", first, second);=
+ ** =A0 =A0 p =3D bufprint(p, end, "/%s", third);
+ ** =A0 =A0 if (p >=3D end) ---> overflow
+ **
+ ** =A0as a convenience, the appended string is zero-t= erminated if there is no overflow.
+ ** =A0(this means that even = if p >=3D end, the content of "buffer" is zero-terminated)
+ **
+ ** =A0vbufprint() is a variant that accepts a va_list= argument
+ **/
+
+static char*
+vb= ufprint( char* =A0 =A0 =A0 =A0buffer,
+ =A0 =A0 =A0 =A0 =A0 char*= =A0 =A0 =A0 =A0buffer_end,
+ =A0 =A0 =A0 =A0 =A0 const char* =A0fmt,
+ =A0 =A0 =A0 =A0 = =A0 va_list =A0 =A0 =A0args )
+{
+ =A0 =A0int =A0len = =3D vsnprintf( buffer, buffer_end - buffer, fmt, args );
+ =A0 = =A0if (len < 0 || buffer+len >=3D buffer_end) {
+ =A0 =A0 =A0 =A0if (buffer < buffer_end)
+ =A0 =A0 =A0 = =A0 =A0 =A0buffer_end[-1] =3D 0;
+ =A0 =A0 =A0 =A0return buffer_e= nd;
+ =A0 =A0}
+ =A0 =A0return buffer + len;
= +}
+
+static char*
+bufprint(char* =A0buffer, char* =A0end, const char* =A0fmt, ... )
+{
+ =A0 =A0va_list =A0args;
+ =A0 =A0char* =A0 = =A0result;
+
+ =A0 =A0va_start(args, fmt);
+ = =A0 =A0result =3D vbufprint(buffer, end, fmt, args);
+ =A0 =A0va_end(args);
+ =A0 =A0return =A0result;
= +}
+
+/** USEFUL DIRECTORY SUPPORT
+ **
=
+ ** =A0bufprint_add_dir() appends the application's directory to = a given bounded buffer
+ **
+ ** =A0bufprint_config_path() appends the applications= ' user-specific configuration directory
+ ** =A0to a bounded = buffer. on Unix this is usually ~/.android, and something a bit more
+ ** =A0complex on Windows
+ **
+ ** =A0bufprint_config= _file() appends the name of a file or directory relative to the
+= ** =A0user-specific configuration directory to a bounded buffer. this real= ly is equivalent
+ ** =A0to concat-ing the config path + path separator + 'suffix&#= 39;
+ **
+ ** =A0bufprint_temp_dir() appends the tempor= ary directory's path to a given bounded buffer
+ **
+ ** =A0bufprint_temp_file() appens the name of a file or directory relativ= e to the
+ ** =A0temporary directory. equivalent to concat-ing th= e temp path + path separator + 'suffix'
+ **/
+
+#ifdef __linux__
+/*static char*
+bufprint_app_di= r(char* =A0buff, char* =A0end)
+{
+ =A0 =A0char =A0 pat= h[1024];
+ =A0 =A0int =A0 =A0len;
+ =A0 =A0char* =A0x;<= /div>
+
+ =A0 =A0len =3D readlink("/proc/self/exe", path, sizeof(path));<= /div>
+ =A0 =A0if (len <=3D 0 || len >=3D (int)sizeof(path)) goto= Fail;
+ =A0 =A0path[len] =3D 0;
+
+ =A0 =A0x= =3D strrchr(path, '/');
+ =A0 =A0if (x =3D=3D 0) goto Fail;
+ =A0 =A0*x =3D 0;
=
+
+ =A0 =A0return bufprint(buff, end, "%s", path);=
+Fail:
+ =A0 =A0fprintf(stderr,"cannot locate app= lication directory\n");
+ =A0 =A0exit(1);
+ =A0 =A0return end;
+}*/
<= div>+
+#elif defined(__APPLE__)
+/* the following hack = is needed in order to build with XCode 3.1
+ * don't ask me w= hy, 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_a= pp_dir(char* =A0buff, char* =A0end)
+{
+ =A0 =A0Process= SerialNumber psn;
+ =A0 =A0CFDictionaryRef =A0 =A0 dict;
+ =A0 =A0CFStringRef = =A0 =A0 =A0 =A0 value;
+ =A0 =A0char =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0s[PATH_MAX];
+ =A0 =A0char* =A0 =A0 =A0 =A0 =A0 =A0 =A0 x;
+
+ =A0 =A0GetCurrentProcess(&psn);
+ =A0 =A0dict =A0=3D ProcessInformationCopyDictionary(&psn, 0xffff= ffff);
+ =A0 =A0value =3D (CFStringRef)CFDictionaryGetValue(dict,=
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 CFSTR("CFBundleExecutable"));
+ =A0 =A0CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingU= TF8);
+ =A0 =A0x =3D strrchr(s, '/');
+ =A0 =A0= if (x =3D=3D 0) goto fail;
+ =A0 =A0*x =3D 0;
+
+ =A0 =A0return bufprint(buff, end, "%s", s);
+fail:
+ =A0 =A0fprintf(stderr,"cannot locate applicati= on directory\n");
+ =A0 =A0exit(1);
+ =A0 =A0retur= n end;
+}
+#elif defined _WIN32
+static char*=
+bufprint_app_dir(char* =A0buff, char* =A0end)
+{
+ =A0= =A0char =A0 appDir[MAX_PATH];
+ int =A0 =A0len;
+ char* =A0sep;
+
+ =A0 =A0len =3D GetModuleFileName( 0, appDir, sizeof(appD= ir)-1 );
+ if (len = =3D=3D 0) {
+ fprint= f(stderr, "PANIC CITY!!\n");
+ exit(1);
+ }
+ if (len >=3D (int)sizeof(appDir)) {
+ len =3D sizeof(appDir)-= 1;
+ =A0 =A0appDir[l= en] =3D 0;
+ =A0 =A0}
+
+ sep =3D strrchr(appDir, '= \\');
+ if (sep)<= /div>
+ =A0*sep =3D 0;
+
+ =A0 =A0return bufprint(buff, end, "%s", appDir= );
+}
+#else
+static char*
+bufprin= t_app_dir(char* =A0buff, char* =A0end)
+{
+ =A0 =A0retu= rn bufprint(buff, end, ".");
+}
+#endif
+
+#define =A0_ANDROID_PATH = =A0 ".android"
+
+/*static char*
+b= ufprint_config_path(char* =A0buff, char* =A0end)
+{
+#i= fdef _WIN32
+ =A0 =A0const char* =A0home =3D getenv("ANDROID_SDK_HOME");=
+ =A0 =A0if (home !=3D NULL) {
+ =A0 =A0 =A0 =A0return= bufprint(buff, end, "%s\\%s", home, _ANDROID_PATH );
+= =A0 =A0} else {
+ =A0 =A0 =A0 =A0char =A0path[MAX_PATH];
+
+ =A0 = =A0 =A0 =A0SHGetFolderPath( NULL, CSIDL_PROFILE,
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 NULL, 0, path);
+
+= =A0 =A0 =A0 =A0return bufprint(buff, end, "%s\\%s", path, _ANDRO= ID_PATH );
+ =A0 =A0}
+#else
+ =A0 =A0const char* =A0home =3D= getenv("ANDROID_SDK_HOME");
+ =A0 =A0if (home =3D=3D N= ULL)
+ =A0 =A0 =A0 =A0home =3D getenv("HOME");
+ =A0 =A0if (home =3D=3D NULL)
+ =A0 =A0 =A0 =A0home =3D "/tmp";
+ =A0 =A0return = bufprint(buff, end, "%s/%s", home, _ANDROID_PATH );
+#e= ndif
+}*/
+
+/*static char*
+bufpri= nt_config_file(char* =A0buff, char* =A0end, const char* =A0suffix)
+{
+ =A0 =A0char* =A0 p;
+ =A0 =A0p =3D bufprint_c= onfig_path(buff, end);
+ =A0 =A0p =3D bufprint(p, end, PATH_SEP &= quot;%s", suffix);
+ =A0 =A0return p;
+}*/
+
+static char*
+bufprint_temp_dir(char* =A0buff, char* =A0end)
+{
+#ifdef _WIN32
+ =A0 =A0char =A0 path[MAX_PATH];
+ =A0= =A0DWORD =A0retval;
+
+ =A0 =A0retval =3D GetTempPath(= sizeof(path), path );
+ =A0 =A0if (retval > sizeof(path) || retval =3D=3D 0) {
= + =A0 =A0 =A0 =A0D( "can't locate TEMP directory" );
+ =A0 =A0 =A0 =A0strncpy(path, "C:\\Temp", sizeof(path) );
+ =A0 =A0}
+ =A0 =A0strncat( path, "\\AndroidEmulator", sizeof(path)-1 );
+ =A0 =A0path_mkdir(path, 0744);
+
+ =A0 =A0ret= urn =A0bufprint(buff, end, "%s", path);
+#else
+ =A0 =A0char path[MAX_PATH];
+ =A0 =A0const char* =A0tmppath =3D getenv("ANDROID_TMP");
+ =A0 =A0if (!tmppath) {
+ =A0 =A0 =A0 =A0const char* us= er =3D getenv("USER");
+ =A0 =A0 =A0 =A0if (user =3D=3D= NULL || user[0] =3D=3D '\0')
+ =A0 =A0 =A0 =A0 =A0 =A0user =3D "unknown";
+
+ =A0 =A0 =A0 =A0snprintf(path, sizeof path, "/tmp/android-%s&q= uot;, user);
+ =A0 =A0 =A0 =A0tmppath =3D path;
+ =A0 = =A0}
+ =A0 =A0mkdir(tmppath, 0744);
+ =A0 =A0return =A0bufprint(buff, end, "%s", tmppath );
+#endif
+}
+
+static char*
+b= ufprint_temp_file(char* =A0buff, char* =A0end, const char* =A0suffix)
=
+{
+ =A0 =A0char* =A0p;
+ =A0 =A0p =3D bufprint_temp_dir(buff, = end);
+ =A0 =A0p =3D bufprint(p, end, PATH_SEP "%s", su= ffix);
+ =A0 =A0return p;
+}
+
+/* = Tempfile support */
+struct TempFile
+{
+ =A0 =A0const char* =A0name;<= /div>
+ =A0 =A0TempFile* =A0 =A0next;
+};
+
+static void =A0 =A0 =A0 tempfile_atexit(void);
+static TempFi= le* =A0_all_tempfiles;
+
+TempFile*
+tempfile_create( void )
+{=
+ =A0 =A0TempFile* =A0 =A0tempfile;
+ =A0 =A0const cha= r* =A0tempname =3D NULL;
+
+#ifdef _WIN32
+ = =A0 =A0char =A0temp_namebuff[MAX_PATH];
+ =A0 =A0char =A0temp_dir[MAX_PATH];
+ =A0 =A0char =A0*p =3D= temp_dir, *end =3D p + sizeof(temp_dir);
+ =A0 =A0UINT =A0retval= ;
+
+ =A0 =A0p =3D bufprint_temp_dir( p, end );
+ =A0 =A0if (p >=3D end) {
+ =A0 =A0 =A0 =A0D( "TEMP directory path is too long" );
+ =A0 =A0 =A0 =A0return NULL;
+ =A0 =A0}
+
=
+ =A0 =A0retval =3D GetTempFileName(temp_dir, "TMP", 0, temp= _namebuff);
+ =A0 =A0if (retval =3D=3D 0) {
+ =A0 =A0 =A0 =A0D( "can'= ;t create temporary file in '%s'", temp_dir );
+ =A0= =A0 =A0 =A0return NULL;
+ =A0 =A0}
+
+ =A0 = =A0tempname =3D temp_namebuff;
+#else
+#define =A0TEMPLATE =A0"/tmp/.android-emulator-= XXXXXX"
+ =A0 =A0int =A0 tempfd =3D -1;
+ =A0 =A0c= har =A0template[512];
+ =A0 =A0char =A0*p =3D template, *end =3D = p + sizeof(template);
+
+ =A0 =A0p =3D bufprint_temp_file( p, end, "emulator-= XXXXXX" );
+ =A0 =A0if (p >=3D end) {
+ =A0 =A0= =A0 =A0D( "Xcannot create temporary file in /tmp/android !!" );<= /div>
+ =A0 =A0 =A0 =A0return NULL;
+ =A0 =A0}
+
+ =A0 =A0D( "template: %s",= template );
+ =A0 =A0tempfd =3D mkstemp( template );
+= =A0 =A0if (tempfd < 0) {
+ =A0 =A0 =A0 =A0D("cannot crea= te temporary file in /tmp/android !!");
+ =A0 =A0 =A0 =A0return NULL;
+ =A0 =A0}
+ =A0 =A0= close(tempfd);
+ =A0 =A0tempname =3D template;
+#endif<= /div>
+ =A0 =A0tempfile =3D malloc( sizeof(*tempfile) + strlen(tempname= ) + 1 );
+ =A0 =A0tempfile->name =3D (char*)(tempfile + 1);
+ =A0 =A0strcpy( (char*)tempfile->name, tempname );
+
+ =A0 =A0tempfile->next =3D _all_tempfiles;
+ =A0 =A0_a= ll_tempfiles =3D tempfile;
+
+ =A0 =A0if ( !tempfile-&g= t;next ) {
+ =A0 =A0 =A0 =A0atexit( tempfile_atexit );
+ =A0 =A0}
=
+
+ =A0 =A0return tempfile;
+}
+
+const char*
+tempfile_path(TempFile* =A0temp)
+{
+ =A0 =A0return temp ? temp->name : NULL;
+}
+
+void
+tempfile_close(TempFile* =A0= tempfile)
+{
+#ifdef _WIN32
+ =A0 =A0DeleteFi= le(tempfile->name);
+#else
+ =A0 =A0unlink(tempfile-= >name);
+#endif
+}
+
+/** TEMP FILE CLEANUP
+ **
+ **/
+
+/* we don't expect to= use many temporary files */
+#define MAX_ATEXIT_FDS =A016
+
+typedef struct {
+ =A0 =A0int =A0 count;
<= div>+ =A0 =A0int =A0 fds[ MAX_ATEXIT_FDS ];
+} AtExitFds;
+
+static void
+atexit_fds_add( AtExitFds* =A0t, int= =A0fd )
+{
+ =A0 =A0if (t->count < MAX_ATEXIT_FDS)
+= =A0 =A0 =A0 =A0t->fds[t->count++] =3D fd;
+ =A0 =A0else {<= /div>
+ =A0 =A0 =A0 =A0D("%s: over %d calls. Program exit may not = cleanup all temporary files",
+ =A0 =A0 =A0 =A0 =A0 =A0__FUNCTION__, MAX_ATEXIT_FDS);
+ = =A0 =A0}
+}
+
+static void
+atexit_= fds_del( AtExitFds* =A0t, int =A0fd )
+{
+ =A0 =A0int = =A0nn;
+ =A0 =A0for (nn =3D 0; nn < t->count; nn++)
+ =A0 =A0 =A0 =A0if (t->fds[nn] =3D=3D fd) {
+ =A0 =A0 = =A0 =A0 =A0 =A0/* move the last element to the current position */
+ =A0 =A0 =A0 =A0 =A0 =A0t->count =A0-=3D 1;
+ =A0 =A0 =A0 = =A0 =A0 =A0t->fds[nn] =3D t->fds[t->count];
+ =A0 =A0 =A0 =A0 =A0 =A0break;
+ =A0 =A0 =A0 =A0}
+}
+
+static void
+atexit_fds_close_all( AtE= xitFds* =A0t )
+{
+ =A0 =A0int =A0nn;
+ =A0 = =A0for (nn =3D 0; nn < t->count; nn++)
+ =A0 =A0 =A0 =A0close(t->fds[nn]);
+}
+
<= div>+static AtExitFds =A0 _atexit_fds[1];
+
+void
=
+atexit_close_fd(int =A0fd)
+{
+ =A0 =A0if (fd >= ;=3D 0)
+ =A0 =A0 =A0 =A0atexit_fds_add(_atexit_fds, fd);
+}
+<= /div>
+void
+atexit_close_fd_remove(int =A0fd)
+{
+ =A0 =A0if (fd >=3D 0)
+ =A0 =A0 =A0 =A0atexit_fds_d= el(_atexit_fds, fd);
+}
+
+static void
+tempfile_atexit( void= )
+{
+ =A0 =A0TempFile* =A0tempfile;
+
=
+ =A0 =A0atexit_fds_close_all( _atexit_fds );
+
+ = =A0 =A0for (tempfile =3D _all_tempfiles; tempfile; tempfile =3D tempfile-&g= t;next)
+ =A0 =A0 =A0 =A0tempfile_close(tempfile);
+}
+
+#ifdef _WIN32
+# =A0include <process.h>
= +# =A0include <windows.h>
+# =A0include <tlhelp32.h><= /div>
+#else
+# =A0include <sys/types.h>
+# =A0include = <unistd.h>
+# =A0include <signal.h>
+#endif=
+
+
+#ifndef CHECKED
+# =A0ifdef _= WIN32
+# =A0 =A0define =A0 CHECKED(ret, call) =A0 =A0(ret) =3D (call)
<= div>+# =A0else
+# =A0 =A0define =A0 CHECKED(ret, call) =A0 =A0do = { (ret) =3D (call); } while ((ret) < 0 && errno =3D=3D EINTR)
+# =A0endif
+#endif
+
+/** FILE LOCKS SUPPORT
+ **
<= div>+ ** a FileLock is useful to prevent several emulator instances from us= ing the same
+ ** writable file (e.g. the userdata.img disk image= s).
+ **
+ ** create a FileLock object with filelock_create(), i= this function should return NULL
+ ** only if the corresponding f= ile 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 tha= t can linger
+ ** when the emulator crashes unexpectedly, and wil= l happily clean them for you
+ **
+ ** =A0here's ho= w it works, three files are used:
+ ** =A0 =A0 file =A0- the data file accessed by the emulator
+ ** =A0 =A0 lock =A0- a lock file =A0(file + '.lock')
= + ** =A0 =A0 temp =A0- a temporary file make unique with mkstemp
= + **
+ ** =A0when locking:
+ ** =A0 =A0 =A0create 'temp' and store our pid in it
+ ** =A0 =A0 =A0attemp to link 'lock' to 'temp'
+ ** =A0 =A0 =A0 =A0 if the link succeeds, we obtain the lock
+ = ** =A0 =A0 =A0unlink 'temp'
+ **
+ ** =A0when unlocking:
+ ** =A0 =A0 =A0unlin= k 'lock'
+ **
+ **
+ ** =A0on Windows= , 'lock' is a directory name. locking is equivalent to
+ = ** =A0creating it...
+ **
+ **/
+
+struct FileLock
= +{
+ =A0const char* =A0file;
+ =A0const char* =A0lock;<= /div>
+ =A0char* =A0 =A0 =A0 =A0temp;
+ =A0int =A0 =A0 =A0 = =A0 =A0locked;
+ =A0FileLock* =A0 =A0next;
+};
+
+/* used to cleanup all locks at emulator ex= it */
+static FileLock* =A0 _all_filelocks;
+
+
+#define =A0LOCK_NAME =A0 ".lock"
+#define= =A0TEMP_NAME =A0 ".tmp-XXXXXX"
+
+#ifdef _WIN32
+#define =A0PIDFILE_NAME =A0"= ;pid"
+#endif
+
+/* returns 0 on success= , -1 on failure */
+static int
+filelock_lock( FileLock= * =A0lock )
+{
+ =A0 =A0int =A0 =A0ret;
+#ifdef _WIN32
+ =A0 =A0int =A0pidfile_fd =3D -1;
+
+ =A0 =A0ret = =3D _mkdir( lock->lock );
+ =A0 =A0if (ret < 0) {
+ =A0 =A0 =A0 =A0if (errno =3D=3D ENOENT) {
+ =A0 =A0 =A0 =A0 =A0 =A0D( "could not access directory '%s&#= 39;, check path elements", lock->lock );
+ =A0 =A0 =A0 = =A0 =A0 =A0return -1;
+ =A0 =A0 =A0 =A0} else if (errno !=3D EEXI= ST) {
+ =A0 =A0 =A0 =A0 =A0 =A0D( "_mkdir(%s): %s", loc= k->lock, strerror(errno) );
+ =A0 =A0 =A0 =A0 =A0 =A0return -1;
+ =A0 =A0 =A0 =A0}
=
+
+ =A0 =A0 =A0 =A0/* if we get here, it's because the .= lock directory already exists */
+ =A0 =A0 =A0 =A0/* check to see= if there is a pid file in it =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 *= /
+ =A0 =A0 =A0 =A0D("directory '%s' already exist, waiting= a bit to ensure that no other emulator instance is starting", lock-&g= t;lock );
+ =A0 =A0 =A0 =A0{
+ =A0 =A0 =A0 =A0 =A0 =A0i= nt =A0_sleep =3D 200;
+ =A0 =A0 =A0 =A0 =A0 =A0int =A0tries;
+
+ =A0 =A0 =A0 = =A0 =A0 =A0for ( tries =3D 4; tries > 0; tries-- )
+ =A0 =A0 = =A0 =A0 =A0 =A0{
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pidfile_fd =3D = open( lock->temp, O_RDONLY );
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (pidfile_fd >=3D 0)
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
+
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0Sleep( _sleep );
+ =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0_sleep *=3D 2;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0= =A0 =A0 =A0}
+
+ =A0 =A0 =A0 =A0if (pidfile_fd < 0) {
+ =A0 = =A0 =A0 =A0 =A0 =A0D( "no pid file in '%s', assuming stale dir= ectory", lock->lock );
+ =A0 =A0 =A0 =A0}
+ =A0= =A0 =A0 =A0else
+ =A0 =A0 =A0 =A0{
+ =A0 =A0 =A0 =A0 =A0 =A0/* read the pidfile, and check wether the cor= responding process is still running */
+ =A0 =A0 =A0 =A0 =A0 =A0c= har =A0 =A0 =A0 =A0 =A0 =A0buf[16];
+ =A0 =A0 =A0 =A0 =A0 =A0int = =A0 =A0 =A0 =A0 =A0 =A0 len, lockpid;
+ =A0 =A0 =A0 =A0 =A0 =A0HA= NDLE =A0 =A0 =A0 =A0 =A0processSnapshot;
+ =A0 =A0 =A0 =A0 =A0 =A0PROCESSENTRY32 =A0pe32;
+ =A0 =A0 = =A0 =A0 =A0 =A0int =A0 =A0 =A0 =A0 =A0 =A0 is_locked =3D 0;
+
+ =A0 =A0 =A0 =A0 =A0 =A0len =3D read( pidfile_fd, buf, sizeof(buf)-= 1 );
+ =A0 =A0 =A0 =A0 =A0 =A0if (len < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0D( "could not read pid file '= ;%s'", lock->temp );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0close( pidfile_fd );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -= 1;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0= buf[len] =3D 0;
+ =A0 =A0 =A0 =A0 =A0 =A0lockpid =A0=3D atoi(buf);
+
+ =A0 =A0 =A0 =A0 =A0 =A0/* PID 0 is the IDLE process, and 0 is returned= in case of invalid input */
+ =A0 =A0 =A0 =A0 =A0 =A0if (lockpid= =3D=3D 0)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0lockpid =3D -1;
+
+ =A0 =A0 =A0 =A0 =A0 =A0close( pidfile_fd );
+<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0pe32.dwSize =A0 =A0 =3D sizeof( PROCESSE= NTRY32 );
+ =A0 =A0 =A0 =A0 =A0 =A0processSnapshot =3D CreateTool= help32Snapshot( TH32CS_SNAPPROCESS, 0 );
+
+ =A0 =A0 =A0 =A0 =A0 =A0if ( processSnapshot =3D=3D INVAL= ID_HANDLE_VALUE ) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0D( "cou= ld not retrieve the list of currently active processes\n" );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0is_locked =3D 1;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0else if = ( !Process32First( processSnapshot, &pe32 ) )
+ =A0 =A0 =A0 = =A0 =A0 =A0{
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0D( "could not = retrieve first process id\n" );
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0CloseHandle( processSnapshot );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0is_locked =3D 1;
+ =A0 =A0 = =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0else
+ =A0 = =A0 =A0 =A0 =A0 =A0{
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0do {
<= div>+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (pe32.th32ProcessID =3D=3D = lockpid) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0is_locked =3D 1;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0} while (Process32Next( processSnapshot, &pe32 ) );
+<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0CloseHandle( processSnapshot );
+ =A0 =A0 =A0 =A0 =A0 =A0}
+
+ =A0 =A0 =A0 =A0 =A0= =A0if (is_locked) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0D( "th= e file '%s' is locked by process ID %d\n", lock->file, lock= pid );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -1;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0}
+ = =A0 =A0}
+
+ =A0 =A0/* write our PID into the pid file = */
+ =A0 =A0pidfile_fd =3D open( lock->temp, O_WRONLY | O_CREA= T | O_TRUNC );
+ =A0 =A0if (pidfile_fd < 0) {
+ =A0 =A0 =A0 =A0if (errno =3D=3D EACCES) {
+ =A0 =A0 =A0 = =A0 =A0 =A0if ( path_delete_file( lock->temp ) < 0 ) {
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0D( "could not remove '%s': %s\n= ", lock->temp, strerror(errno) );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -1;
+ =A0 =A0 =A0 = =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0pidfile_fd =3D open( lock-&= gt;temp, O_WRONLY | O_CREAT | O_TRUNC );
+ =A0 =A0 =A0 =A0}
=
+ =A0 =A0 =A0 =A0if (pidfile_fd < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0D( "could not create '%s': %s\n"= , lock->temp, strerror(errno) );
+ =A0 =A0 =A0 =A0 =A0 =A0retu= rn -1;
+ =A0 =A0 =A0 =A0}
+ =A0 =A0}
+
<= div>+ =A0 =A0{
+ =A0 =A0 =A0 =A0char =A0buf[16];
+ =A0 =A0 =A0 =A0sprintf( buf, "%ld", GetCurrentProcessId() = );
+ =A0 =A0 =A0 =A0ret =3D write( pidfile_fd, buf, strlen(buf) )= ;
+ =A0 =A0 =A0 =A0close(pidfile_fd);
+ =A0 =A0 =A0 =A0= if (ret < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0D( "could not wri= te PID to '%s'\n", lock->temp );
+ =A0 =A0 =A0 =A0 =A0 =A0return -1;
+ =A0 =A0 =A0 =A0}
=
+ =A0 =A0}
+
+ =A0 =A0lock->locked =3D 1;
=
+ =A0 =A0return 0;
+#else
+ =A0 =A0int =A0 =A0temp= _fd =3D -1;
+ =A0 =A0int =A0 =A0lock_fd =3D -1;
+ =A0 =A0int =A0 =A0rc, tries, _sleep;
+ =A0 =A0FILE* =A0f = =3D NULL;
+ =A0 =A0char =A0 pid[8];
+ =A0 =A0struct sta= t =A0st_temp;
+
+ =A0 =A0strcpy( lock->temp, lock-&g= t;file );
+ =A0 =A0strcat( lock->temp, TEMP_NAME );
+ =A0 =A0temp_fd =3D mkstemp( lock->temp );
+
+= =A0 =A0if (temp_fd < 0) {
+ =A0 =A0 =A0 =A0D("cannot cre= ate locking temp file '%s'", lock->temp );
+ =A0 = =A0 =A0 =A0goto Fail;
+ =A0 =A0}
+
+ =A0 =A0sprintf( pid, "%d"= , getpid() );
+ =A0 =A0ret =3D write( temp_fd, pid, strlen(pid)+1= );
+ =A0 =A0if (ret < 0) {
+ =A0 =A0 =A0 =A0D( &quo= t;cannot write to locking temp file '%s'", lock->temp);
+ =A0 =A0 =A0 =A0goto Fail;
+ =A0 =A0}
+ =A0 =A0cl= ose( temp_fd );
+ =A0 =A0temp_fd =3D -1;
+
+ = =A0 =A0CHECKED(rc, lstat( lock->temp, &st_temp ));
+ =A0 = =A0if (rc < 0) {
+ =A0 =A0 =A0 =A0D( "can't properly stat our locking temp file = 9;%s'", lock->temp );
+ =A0 =A0 =A0 =A0goto Fail;
+ =A0 =A0}
+
+ =A0 =A0/* now attempt to link the= temp file to the lock file */
+ =A0 =A0_sleep =3D 0;
+ =A0 =A0for ( tries =3D 4; tries >= ; 0; tries-- )
+ =A0 =A0{
+ =A0 =A0 =A0 =A0struct stat = =A0st_lock;
+ =A0 =A0 =A0 =A0int =A0 =A0 =A0 =A0 =A0rc;
+
+ =A0 =A0 =A0 =A0if (_sleep > 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0if (_sleep > 2000000) {
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0D( "cannot acquire lock file '%s'&q= uot;, lock->lock );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto Fail= ;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0u= sleep( _sleep );
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0_sleep +=3D 200000;
+
+ =A0 =A0 =A0 =A0/* the return value of link() is buggy o= n NFS */
+ =A0 =A0 =A0 =A0CHECKED(rc, link( lock->temp, lock-&= gt;lock ));
+
+ =A0 =A0 =A0 =A0CHECKED(rc, lstat( lock->lock, &st_lock ));
+ =A0 =A0 =A0 =A0if (rc =3D=3D 0 &&
+ =A0 =A0 =A0 =A0 = =A0 =A0st_temp.st_rdev =3D=3D st_lock.st_rdev &&
+ =A0 = =A0 =A0 =A0 =A0 =A0st_temp.st_ino =A0=3D=3D st_lock.st_ino =A0)
+ =A0 =A0 =A0 =A0{
+ =A0 =A0 =A0 =A0 =A0 =A0/* SUCCESS */
+ =A0 =A0 =A0 =A0 =A0 =A0lock->locked =3D 1;
+ =A0 =A0= =A0 =A0 =A0 =A0CHECKED(rc, unlink( lock->temp ));
+ =A0 =A0 = =A0 =A0 =A0 =A0return 0;
+ =A0 =A0 =A0 =A0}
+
+ =A0 =A0 =A0 =A0/* if we get there, it means that the link() c= all failed */
+ =A0 =A0 =A0 =A0/* check the lockfile to see if it= is stale =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
+ =A0 =A0 =A0 =A0if (rc = =3D=3D 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0char =A0 =A0buf[16];
+ =A0 =A0 =A0 =A0 =A0 =A0time_t =A0now;
+ =A0 =A0 =A0 =A0 = =A0 =A0int =A0 =A0 lockpid =3D 0;
+ =A0 =A0 =A0 =A0 =A0 =A0int = =A0 =A0 lockfd;
+ =A0 =A0 =A0 =A0 =A0 =A0int =A0 =A0 stale =3D 2;= =A0/* means don't know */
+ =A0 =A0 =A0 =A0 =A0 =A0struct st= at =A0st;
+
+ =A0 =A0 =A0 =A0 =A0 =A0CHECKED(rc, time( &now));
+ =A0 =A0 =A0 =A0 =A0 =A0st.st_mtime =3D now - 120;
+
+ =A0 =A0 =A0 =A0 =A0 =A0CHECKED(lockfd, open( lock->lock,O_RDONLY= ));
+ =A0 =A0 =A0 =A0 =A0 =A0if ( lockfd >=3D 0 ) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int =A0len;
+
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0CHECKED(len, read( lockfd, buf, sizeof(buf)-= 1 ));
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0buf[len] =3D 0;
= + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0lockpid =3D atoi(buf);
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0CHECKED(rc, fstat( lockfd, &st ))= ;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (rc =3D=3D 0)
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0now =3D st.st_atime;
+
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0CHECKED(rc, close(lockfd));
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0/* if th= ere is a PID, check that it is still alive */
+ =A0 =A0 =A0 =A0 = =A0 =A0if (lockpid > 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0CHE= CKED(rc, kill( lockpid, 0 ));
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if= (rc =3D=3D 0 || errno =3D=3D EPERM) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0stale =3D 0;
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0} else if (rc < 0 && errno =3D=3D ESR= CH) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0stale =3D 1;
=
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0= }
+ =A0 =A0 =A0 =A0 =A0 =A0if (stale =3D=3D 2) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* no pid, stale if the file is older= than 1 minute */
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0stale =3D (now= >=3D st.st_mtime + 60);
+ =A0 =A0 =A0 =A0 =A0 =A0}
= +
+ =A0 =A0 =A0 =A0 =A0 =A0if (stale) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0D( "removing stale lockfile '%s&#= 39;", lock->lock );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0CHEC= KED(rc, unlink( lock->lock ));
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0_sleep =3D 0;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tries++;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0}
+ = =A0 =A0}
+ =A0 =A0D("file '%s' is already in use by = another process", lock->file );
+
+Fail:
<= div>+ =A0 =A0if (f)
+ =A0 =A0 =A0 =A0fclose(f);
+
+ =A0 =A0if (temp_fd >= =3D 0) {
+ =A0 =A0 =A0 =A0close(temp_fd);
+ =A0 =A0}
+
+ =A0 =A0if (lock_fd >=3D 0) {
+ =A0 =A0 = =A0 =A0close(lock_fd);
+ =A0 =A0}
+
+ =A0 =A0unlink( lock->lock );
+ =A0 =A0unlin= k( lock->temp );
+ =A0 =A0return -1;
+#endif
+}
+
+void
+filelock_release( FileLock* = =A0lock )
+{
+ =A0 =A0if (lock->locked) {
+#ifdef _WIN32<= /div>
+ =A0 =A0 =A0 =A0path_delete_file( (char*)lock->temp );
<= div>+ =A0 =A0 =A0 =A0rmdir( (char*)lock->lock );
+#else
<= div>+ =A0 =A0 =A0 =A0unlink( (char*)lock->lock );
+#endif
+ =A0 =A0 =A0 =A0lock->locked =3D 0;
+ = =A0 =A0}
+}
+
+static void
+fileloc= k_atexit( void )
+{
+ =A0FileLock* =A0lock;
+=
+ =A0for (lock =3D _all_filelocks; lock !=3D NULL; lock =3D lock= ->next)
+ =A0 =A0 filelock_release( lock );
+}
+
+/* create a file lock */
+FileLock*
+filelock_create(= const char* =A0file )
+{
+ =A0 =A0int =A0 =A0file_len = =3D strlen(file);
+ =A0 =A0int =A0 =A0lock_len =3D file_len + sizeof(LOCK_NAME);
+#ifdef _WIN32
+ =A0 =A0int =A0 =A0temp_len =3D lock_len + 1 += sizeof(PIDFILE_NAME);
+#else
+ =A0 =A0int =A0 =A0temp_= len =3D file_len + sizeof(TEMP_NAME);
+#endif
+ =A0 =A0int =A0 =A0total_len =3D sizeof(FileLock) += file_len + lock_len + temp_len + 3;
+
+ =A0 =A0FileLoc= k* =A0lock =3D malloc(total_len);
+
+ =A0 =A0lock->f= ile =3D (const char*)(lock + 1);
+ =A0 =A0memcpy( (char*)lock->file, file, file_len+1 );
+=
+ =A0 =A0lock->lock =3D lock->file + file_len + 1;
+ =A0 =A0memcpy( (char*)lock->lock, file, file_len+1 );
+ = =A0 =A0strcat( (char*)lock->lock, LOCK_NAME );
+
+ =A0 =A0lock->temp =A0 =A0=3D (char*)lock->lock + l= ock_len + 1;
+#ifdef _WIN32
+ =A0 =A0snprintf( (char*)l= ock->temp, temp_len, "%s\\" PIDFILE_NAME, lock->lock );
+#else
+ =A0 =A0lock->temp[0] =3D 0;
+#endif
+ =A0 =A0= lock->locked =3D 0;
+
+ =A0 =A0if (filelock_lock(loc= k) < 0) {
+ =A0 =A0 =A0 =A0free(lock);
+ =A0 =A0 =A0= =A0return NULL;
+ =A0 =A0}
+
+ =A0 =A0lock->next =A0 =A0 =3D _all_filelocks;
+ =A0 =A0_all_filelocks =3D lock;
+
+ =A0 =A0if (loc= k->next =3D=3D NULL)
+ =A0 =A0 =A0 =A0atexit( filelock_atexit = );
+
+ =A0 =A0return lock;
+}
+
+/** PATH HANDLING ROUTINES
+ **
+ ** =A0path_parent() can be used to return the n-level parent of a= given directory
+ ** =A0this understands . and .. when encounter= ed in the input path
+ **/
+
+static __inline__ int
+ispathse= p(int =A0c)
+{
+#ifdef _WIN32
+ =A0 =A0return= (c =3D=3D '/' || c =3D=3D '\\');
+#else
+ =A0 =A0return (c =3D=3D '/');
+#endif
+}
+
+char*
+path_pare= nt( const char* =A0path, int =A0levels )
+{
+ =A0 =A0co= nst char* =A0end =3D path + strlen(path);
+ =A0 =A0char* =A0 =A0 = =A0 =A0result;
+
+ =A0 =A0while (levels > 0) {
+ =A0 =A0 =A0 = =A0const char* =A0base;
+
+ =A0 =A0 =A0 =A0/* trim any = trailing path separator */
+ =A0 =A0 =A0 =A0while (end > path = && ispathsep(end[-1]))
+ =A0 =A0 =A0 =A0 =A0 =A0end--;
+
+ =A0 =A0 =A0 = =A0base =3D end;
+ =A0 =A0 =A0 =A0while (base > path &&= ; !ispathsep(base[-1]))
+ =A0 =A0 =A0 =A0 =A0 =A0base--;
+
+ =A0 =A0 =A0 =A0if (base <=3D path) /* we can't go th= at far */
+ =A0 =A0 =A0 =A0 =A0 =A0return NULL;
+
+ =A0 =A0 = =A0 =A0if (end =3D=3D base+1 && base[0] =3D=3D '.')
+ =A0 =A0 =A0 =A0 =A0 =A0goto Next;
+
+ =A0 =A0 =A0 = =A0if (end =3D=3D base+2 && base[0] =3D=3D '.' && b= ase[1] =3D=3D '.') {
+ =A0 =A0 =A0 =A0 =A0 =A0levels +=3D 1;
+ =A0 =A0 =A0 =A0 = =A0 =A0goto Next;
+ =A0 =A0 =A0 =A0}
+
+ =A0 = =A0 =A0 =A0levels -=3D 1;
+
+ =A0 =A0Next:
+ = =A0 =A0 =A0 =A0end =3D base - 1;
+ =A0 =A0}
+ =A0 =A0result =3D malloc( end-path+1 );
+ =A0 =A0if (result != =3D NULL) {
+ =A0 =A0 =A0 =A0memcpy( result, path, end-path );
+ =A0 =A0 =A0 =A0result[end-path] =3D 0;
+ =A0 =A0}
=
+ =A0 =A0return result;
+}
+
+static char*
+substring_dup( const char= * =A0start, const char* =A0end )
+{
+ =A0 =A0int =A0 = =A0len =A0 =A0=3D end - start;
+ =A0 =A0char* =A0result =3D qemu_= malloc(len+1);
+ =A0 =A0memcpy(result, start, len);
+ =A0 =A0result[len] =3D 0;<= /div>
+ =A0 =A0return result;
+}
+
+int
+path_split( const char* =A0path, char* *pdirname, char* *pbasenam= e )
+{
+ =A0 =A0const char* =A0end =3D path + strlen(path);
+ =A0 =A0const char* =A0last;
+ =A0 =A0char* =A0 =A0 =A0 =A0base= name;
+
+ =A0 =A0/* prepare for errors */
+ = =A0 =A0if (pdirname)
+ =A0 =A0 =A0 =A0*pdirname =3D NULL;
+ =A0 =A0if (pbasename)
+ =A0 =A0 =A0 =A0*pbasename =3D NULL;
+
+ =A0 =A0= /* handle empty path case */
+ =A0 =A0if (end =3D=3D path) {
+ =A0 =A0 =A0 =A0return -1;
+ =A0 =A0}
+
+ =A0 =A0/* strip trailing path separators= */
+ =A0 =A0while (end > path && ispathsep(end[-1]))<= /div>
+ =A0 =A0 =A0 =A0end -=3D 1;
+
+ =A0 =A0/* ha= ndle "/" and degenerate cases like "////" */
+ =A0 =A0if (end =3D=3D path) {
+ =A0 =A0 =A0 =A0return -1;<= /div>
+ =A0 =A0}
+
+ =A0 =A0/* find last separator = */
+ =A0 =A0last =3D end;
+ =A0 =A0while (last > pat= h && !ispathsep(last[-1]))
+ =A0 =A0 =A0 =A0last -=3D 1;
+
+ =A0 =A0/* handle= cases where there is no path separator */
+ =A0 =A0if (last =3D= =3D path) {
+ =A0 =A0 =A0 =A0if (pdirname)
+ =A0 =A0 = =A0 =A0 =A0 =A0*pdirname =A0=3D qemu_strdup(".");
+ =A0 =A0 =A0 =A0if (pbasename)
+ =A0 =A0 =A0 =A0 =A0 =A0*pb= asename =3D substring_dup(path,end);
+ =A0 =A0 =A0 =A0return 0;
+ =A0 =A0}
+
+ =A0 =A0/* handle "/foo&quo= t; */
+ =A0 =A0if (last =3D=3D path+1) {
+ =A0 =A0 =A0 =A0if (pdirname)
+ =A0 =A0 =A0 =A0 =A0 =A0*pdi= rname =A0=3D qemu_strdup("/");
+ =A0 =A0 =A0 =A0if (pba= sename)
+ =A0 =A0 =A0 =A0 =A0 =A0*pbasename =3D substring_dup(pat= h+1,end);
+ =A0 =A0 =A0 =A0return 0;
+ =A0 =A0}
+
+ =A0 =A0/* compute basename */
=
+ =A0 =A0basename =3D substring_dup(last,end);
+ =A0 =A0if (= strcmp(basename, ".") =3D=3D 0 || strcmp(basename, ".."= ) =3D=3D 0) {
+ =A0 =A0 =A0 =A0qemu_free(basename);
+ =A0 =A0 =A0 =A0return -1;=
+ =A0 =A0}
+
+ =A0 =A0if (pbasename)
+ =A0 =A0 =A0 =A0*pbasename =3D basename;
+ =A0 =A0else {
+ =A0 =A0 =A0 =A0qemu_free(basename);
+ =A0 =A0}
+
+ =A0 =A0/* compute dirname */
<= div>+ =A0 =A0if (pdirname !=3D NULL)
+ =A0 =A0 =A0 =A0*pdirname = =3D substring_dup(path,last-1);
+
+ =A0 =A0return 0;
+}
+
+char*
+path_basename( const char* =A0path )
+{
+ =A0 =A0char* =A0basename;
+
+ =A0 =A0if (path= _split(path, NULL, &basename) < 0)
+ =A0 =A0 =A0 =A0return= NULL;
+
+ =A0 =A0return basename;
+}
+
+c= har*
+path_dirname( const char* =A0path )
+{
= + =A0 =A0char* =A0dirname;
+
+ =A0 =A0if (path_split(pa= th, &dirname, NULL) < 0)
+ =A0 =A0 =A0 =A0return NULL;
+
+ =A0 =A0return di= rname;
+}
+
+
+
+
+
+/** MISC FILE AND DIRECTORY HANDLING
+ **/
<= div>+
+ABool
+path_exists( const char* =A0path )
+{
+ =A0 =A0int =A0ret;
+ =A0 =A0CHECKED(ret, access(path, F_O= K));
+ =A0 =A0return (ret =3D=3D 0) || (errno !=3D ENOENT);
=
+}
+
+/* checks that a path points to a regular file */
+A= Bool
+path_is_regular( const char* =A0path )
+{
+ =A0 =A0int =A0 =A0 =A0 =A0 =A0ret;
+ =A0 =A0struct stat =A0s= t;
+
+ =A0 =A0CHECKED(ret, stat(path, &st));
+ =A0 =A0if (ret= < 0)
+ =A0 =A0 =A0 =A0return 0;
+
+ =A0 = =A0return S_ISREG(st.st_mode);
+}
+
+
+/* checks that a path points to a directory */
+ABool
+path_is_dir( const char* =A0path )
+{
+ =A0 =A0int =A0 =A0 =A0 =A0 =A0ret;
+ =A0 =A0struct stat = =A0st;
+
+ =A0 =A0CHECKED(ret, stat(path, &st));
+ =A0 =A0if (ret < 0)
+ =A0 =A0 =A0 =A0return 0;
+
+ =A0 =A0return S_ISD= IR(st.st_mode);
+}
+
+/* checks that one can = read/write a given (regular) file */
+ABool
+path_can_r= ead( const char* =A0path )
+{
+ =A0 =A0int =A0ret;
+ =A0 =A0CHECKED(ret, acce= ss(path, R_OK));
+ =A0 =A0return (ret =3D=3D 0);
+}
+
+ABool
+path_can_write( const char* =A0path )<= /div>
+{
+ =A0 =A0int =A0ret;
+ =A0 =A0CHECKED(ret, access(path, R_OK= ));
+ =A0 =A0return (ret =3D=3D 0);
+}
+
+ABool
+path_can_exec( const char* path )
+{
+ =A0 =A0int =A0ret;
+ =A0 =A0CHECKED(ret, access(path, X_OK));
+ =A0 =A0return (= ret =3D=3D 0);
+}
+
+/* try to make a directo= ry. returns 0 on success, -1 on failure
+ * (error code in errno)= */
+APosixStatus
+path_mkdir( const char* =A0path, int =A0mode = )
+{
+#ifdef _WIN32
+ =A0 =A0(void)mode;
+ =A0 =A0return _mkdir(path);
+#else
+ =A0 =A0int= =A0ret;
+ =A0 =A0CHECKED(ret, mkdir(path, mode));
+ =A0 =A0return re= t;
+#endif
+}
+
+static APosixStatu= s
+path_mkdir_recursive( char* =A0path, unsigned =A0len, int =A0m= ode )
+{
+ =A0 =A0char =A0 =A0 =A0old_c;
+ =A0 =A0int = =A0 =A0 =A0 ret;
+ =A0 =A0unsigned =A0len2;
+
+ =A0 =A0/* get rid of trailing separators */
+ =A0 =A0while (le= n > 0 && ispathsep(path[len-1]))
+ =A0 =A0 =A0 =A0len -=3D 1;
+
+ =A0 =A0if (len = =3D=3D 0) {
+ =A0 =A0 =A0 =A0errno =3D ENOENT;
+ =A0 = =A0 =A0 =A0return -1;
+ =A0 =A0}
+
+ =A0 =A0/= * check that the parent exists, 'len2' is the length of
+ =A0 =A0 * the parent part of the path */
+ =A0 =A0len2 =3D= len-1;
+ =A0 =A0while (len2 > 0 && !ispathsep(path[le= n2-1]))
+ =A0 =A0 =A0 =A0len2 -=3D 1;
+
+ =A0= =A0if (len2 > 0) {
+ =A0 =A0 =A0 =A0old_c =A0 =A0 =A0=3D path[len2];
+ =A0 =A0 = =A0 =A0path[len2] =3D 0;
+ =A0 =A0 =A0 =A0ret =A0 =A0 =A0 =A0=3D = 0;
+ =A0 =A0 =A0 =A0if ( !path_exists(path) ) {
+ =A0 = =A0 =A0 =A0 =A0 =A0/* the parent doesn't exist, so try to create it */<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0ret =3D path_mkdir_recursive( path, len2, mod= e );
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0path[len2] =3D= old_c;
+
+ =A0 =A0 =A0 =A0if (ret < 0)
+ = =A0 =A0 =A0 =A0 =A0 =A0return ret;
+ =A0 =A0}
+
+ =A0 =A0/* at this point, we now the parent exists */
+ =A0 =A0old_c =A0 =A0 =3D path[len];
+ =A0 =A0path[len] = =3D 0;
+ =A0 =A0ret =A0 =A0 =A0 =3D path_mkdir( path, mode );
+ =A0 =A0path[len] =3D old_c;
+
+ =A0 =A0return ret;
+}
+
+/= * ensure that a given directory exists, create it if not,
+ =A0 0= on success, -1 on failure (error code in errno) */
+APosixStatus=
+path_mkdir_if_needed( const char* =A0path, int =A0mode )
+{=
+ =A0 =A0int =A0ret =3D 0;
+
+ =A0 =A0if (!p= ath_exists(path)) {
+ =A0 =A0 =A0 =A0ret =3D path_mkdir(path, mod= e);
+
+ =A0 =A0 =A0 =A0if (ret < 0 && errno =3D=3D ENOENT) {
+ =A0 =A0 =A0 =A0 =A0 =A0char =A0 =A0 =A0temp[MAX_PATH];
+ =A0 = =A0 =A0 =A0 =A0 =A0unsigned =A0len =3D (unsigned)strlen(path);
+<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0if (len > sizeof(temp)-1) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0errno =3D EINVAL;
+ =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0return -1;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0memcpy( temp, path, len );
+ =A0 = =A0 =A0 =A0 =A0 =A0temp[len] =3D 0;
+
+ =A0 =A0 =A0 =A0= =A0 =A0return path_mkdir_recursive(temp, len, mode);
+ =A0 =A0 =A0 =A0}
+ =A0 =A0}
+ =A0 =A0return 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* =A0path, uint64_t = =A0*psize )
+{
+#ifdef _WIN32
+ =A0 =A0/* avo= id _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */
+ =A0 =A0/* do not use OpenFile() because it has strange search behaviour = that could */
+ =A0 =A0/* result in getting the size of a different file */
+ =A0 =A0LARGE_INTEGER =A0size;
+ =A0 =A0HANDLE =A0file =3D Cre= ateFile( /* lpFilename */ =A0 =A0 =A0 =A0path,
+ =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* dwDesiredAccess */ =A0 GENER= IC_READ,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* dwSha= redMode */ =A0 =A0 FILE_SHARE_READ|FILE_SHARE_WRITE,
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* lpSecurityAttributes= */ =A0NULL,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 /* dwCreationDisposition */ OPEN_EXISTING,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* dwFla= gsAndAttributes */ =A00,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 /* hTemplateFile */ =A0 =A0 =A0NULL );
+ = =A0 =A0if (file =3D=3D INVALID_HANDLE_VALUE) {
+ =A0 =A0 =A0 =A0/= * ok, just to play fair */
+ =A0 =A0 =A0 =A0errno =3D ENOENT;
+ =A0 =A0 =A0 =A0return -= 1;
+ =A0 =A0}
+ =A0 =A0if (!GetFileSizeEx(file, &si= ze)) {
+ =A0 =A0 =A0 =A0/* maybe we tried to get the size of a pi= pe or something like that ? */
+ =A0 =A0 =A0 =A0*psize =3D 0;
+ =A0 =A0}
+ =A0 = =A0else {
+ =A0 =A0 =A0 =A0*psize =3D (uint64_t) size.QuadPart;
+ =A0 =A0}
+ =A0 =A0CloseHandle(file);
+ =A0 = =A0return 0;
+#else
+ =A0 =A0int =A0 =A0ret;
+ =A0 =A0struct stat =A0st;
+<= /div>
+ =A0 =A0CHECKED(ret, stat(path, &st));
+ =A0 =A0if= (ret =3D=3D 0) {
+ =A0 =A0 =A0 =A0*psize =3D (uint64_t) st.st_si= ze;
+ =A0 =A0}
+ =A0 =A0return ret;
+#endif
+}
+
+/*
+static ABool=
+path_is_absolute( const char* =A0path )
+{
= +#ifdef _WIN32
+ =A0 =A0if (path =3D=3D NULL)
+ =A0 =A0= =A0 =A0return 0;
+
+ =A0 =A0if (path[0] =3D=3D '/' || path[0] =3D=3D = '\\')
+ =A0 =A0 =A0 =A0return 1;
+
+*= / =A0 =A0/* 'C:' is always considered to be absolute
+ = =A0 =A0 * even if used with a relative path like C:foo which
+ =A0 =A0 * is different from C:\foo
+ =A0 =A0 */
= +/* =A0 =A0if (path[0] !=3D 0 && path[1] =3D=3D ':')
<= div>+ =A0 =A0 =A0 =A0return 1;
+
+ =A0 =A0return 0;
+#else
+ =A0 =A0return (path !=3D NULL && path[0] = =3D=3D '/');
+#endif
+}
+*/
+
+/** OTHER FI= LE UTILITIES
+ **
+ ** =A0path_empty_file() creates an = empty file at a given path location.
+ ** =A0if the file already = exists, it is truncated without warning
+ **
+ ** =A0path_copy_file() copies one file into another.<= /div>
+ **
+ ** =A0both functions return 0 on success, and -1= on error
+ **/
+
+APosixStatus
+pa= th_empty_file( const char* =A0path )
+{
+#ifdef _WIN32
+ =A0 =A0int =A0fd =3D _creat( p= ath, S_IWRITE );
+#else
+ =A0 =A0/* on Unix, only allow= the owner to read/write, since the file *
+ =A0 =A0 * may contai= n some personal data we don't want to see exposed */
+ =A0 =A0int =A0fd =3D creat(path, S_IRUSR | S_IWUSR);
+#end= if
+ =A0 =A0if (fd >=3D 0) {
+ =A0 =A0 =A0 =A0close(= fd);
+ =A0 =A0 =A0 =A0return 0;
+ =A0 =A0}
+ = =A0 =A0return -1;
+}
+
+APosixStatus
+path_copy_file( const char* =A0de= st, const char* =A0source )
+{
+ =A0 =A0int =A0fd, fs, = result =3D -1;
+
+ =A0 =A0/* if the destination doesn&#= 39;t exist, create it */
+ =A0 =A0if ( access(source, F_OK) =A0< 0 ||
+ =A0 =A0 = =A0 =A0 path_empty_file(dest) < 0) {
+ =A0 =A0 =A0 =A0return -= 1;
+ =A0 =A0}
+
+ =A0 =A0if ( access(source, = R_OK) < 0 ) {
+ =A0 =A0 =A0 =A0//D("%s: source file is un-readable: %s\n",
+ =A0 =A0 =A0 =A0// =A0__FUNCTION__, source);
+ =A0 =A0 =A0 = =A0return -1;
+ =A0 =A0}
+
+#ifdef _WIN32
+ =A0 =A0fd =3D _open(dest, _O_RDWR | _O_BINARY);
+ =A0 = =A0fs =3D _open(source, _O_RDONLY | =A0_O_BINARY);
+#else
+ =A0 =A0fd =3D creat(dest, S_IRUSR | S_IWUSR);
=
+ =A0 =A0fs =3D open(source, S_IREAD);
+#endif
+ = =A0 =A0if (fs >=3D 0 && fd >=3D 0) {
+ =A0 =A0 =A0 = =A0char buf[4096];
+ =A0 =A0 =A0 =A0ssize_t total =3D 0;
+ =A0 =A0 =A0 =A0ssize_t n;=
+ =A0 =A0 =A0 =A0result =3D 0; /* success */
+ =A0 =A0= =A0 =A0while ((n =3D read(fs, buf, 4096)) > 0) {
+ =A0 =A0 = =A0 =A0 =A0 =A0if (write(fd, buf, n) !=3D n) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* write failed. Make it return -1 so= that an
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 * empty file be create= d. */
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0//D("Failed to copy &= #39;%s' to '%s': %s (%d)",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// =A0 =A0 =A0 source, dest, strerror= (errno), errno);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0result =3D -1;<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
+ =A0 =A0 =A0 = =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0total +=3D n;
+ = =A0 =A0 =A0 =A0}
+ =A0 =A0}
+
+ =A0 =A0if (fs >=3D 0) {
+ =A0 =A0 =A0 =A0close(fs);
+ =A0 =A0}
+ =A0 =A0if (f= d >=3D 0) {
+ =A0 =A0 =A0 =A0close(fd);
+ =A0 =A0}
+ =A0 =A0return result;
+}
+
+
+APosixStatus
+path_delete_f= ile( const char* =A0path )
+{
+#ifdef _WIN32
= + =A0 =A0int =A0ret =3D _unlink( path );
+ =A0 =A0if (ret =3D=3D = -1 && errno =3D=3D EACCES) {
+ =A0 =A0 =A0 =A0/* a first call to _unlink will fail if the file is s= et read-only */
+ =A0 =A0 =A0 =A0/* we can however try to change = its mode first and call unlink =A0 =A0*/
+ =A0 =A0 =A0 =A0/* agai= n... =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 */
+ =A0 =A0 =A0 =A0ret =3D _chmod( path, _S_IREAD | _S_IWRITE );
+ =A0 =A0 =A0 =A0if (ret =3D=3D 0)
+ =A0 =A0 =A0 =A0 =A0 =A0re= t =3D _unlink( path );
+ =A0 =A0}
+ =A0 =A0return ret;<= /div>
+#else
+ =A0 =A0return =A0unlink(path);
+#endif
+}
+
+
+void*
+path_load_file(const char *fn, size_t =A0*pSize)
+{
= + =A0 =A0char* =A0data;
+ =A0 =A0int =A0 =A0sz;
+ =A0 = =A0int =A0 =A0fd;
+
+ =A0 =A0if (pSize)
+ =A0 =A0 =A0 =A0*pSize =3D = 0;
+
+ =A0 =A0data =A0 =3D NULL;
+
= + =A0 =A0fd =3D open(fn, O_BINARY | O_RDONLY);
+ =A0 =A0if(fd <= ; 0) return NULL;
+
+ =A0 =A0do {
+ =A0 =A0 =A0 =A0sz =3D lseek(fd, 0, SE= EK_END);
+ =A0 =A0 =A0 =A0if(sz < 0) break;
+
<= div>+ =A0 =A0 =A0 =A0if (pSize)
+ =A0 =A0 =A0 =A0 =A0 =A0*pSize = =3D (size_t) sz;
+
+ =A0 =A0 =A0 =A0if (lseek(fd, 0, SE= EK_SET) !=3D 0)
+ =A0 =A0 =A0 =A0 =A0 =A0break;
+
+ =A0 =A0 =A0 = =A0data =3D (char*) malloc(sz + 1);
+ =A0 =A0 =A0 =A0if(data =3D= =3D NULL) break;
+
+ =A0 =A0 =A0 =A0if (read(fd, data, = sz) !=3D sz)
+ =A0 =A0 =A0 =A0 =A0 =A0break;
+
+ =A0 =A0 =A0 =A0close(fd);
+ =A0 =A0 =A0 =A0dat= a[sz] =3D 0;
+
+ =A0 =A0 =A0 =A0return data;
= + =A0 =A0} while (0);
+
+ =A0 =A0close(fd);
+=
+ =A0 =A0if(data !=3D NULL)
+ =A0 =A0 =A0 =A0free(data);
+
+ =A0 =A0return NUL= L;
+}
+
+#ifdef _WIN32
+# =A0define= DIR_SEP =A0';'
+#else
+# =A0define DIR_SEP =A0= ':'
+#endif
+
+char*
+path_search_exec( cons= t char* filename )
+{
+ =A0 =A0const char* sysPath =3D = getenv("PATH");
+ =A0 =A0char =A0 =A0 =A0 =A0temp[PATH_= MAX];
+ =A0 =A0const char* p;
+
+ =A0 =A0/* If the file conta= ins a directory separator, don't search */
+#ifdef _WIN32
+ =A0 =A0if (strchr(filename, '/') !=3D NULL || strchr(filen= ame, '\\') !=3D NULL) {
+#else
+ =A0 =A0if (strchr(filename, '/') !=3D NULL)= {
+#endif
+ =A0 =A0 =A0 =A0if (path_exists(filename)) = {
+ =A0 =A0 =A0 =A0 =A0 =A0return strdup(filename);
+ = =A0 =A0 =A0 =A0} else {
+ =A0 =A0 =A0 =A0 =A0 =A0return NULL;
+ =A0 =A0 =A0 =A0}
+ =A0 =A0}
+
+ =A0 =A0/* If system path is empty, don= 't search */
+ =A0 =A0if (sysPath =3D=3D NULL || sysPath[0] = =3D=3D '\0') {
+ =A0 =A0 =A0 =A0return NULL;
+ =A0 =A0}
+
+ =A0 =A0/* Count the number of non-e= mpty items in the system path
+ =A0 =A0 * Items are separated by = DIR_SEP, and two successive separators
+ =A0 =A0 * correspond to = an empty item that will be ignored.
+ =A0 =A0 * Also compute the required string storage length. */
<= div>+ =A0 =A0p =A0 =A0 =A0 =3D sysPath;
+
+ =A0 =A0whil= e (*p) {
+ =A0 =A0 =A0 =A0char* p2 =3D strchr(p, DIR_SEP);
<= div>+ =A0 =A0 =A0 =A0int =A0 len;
+ =A0 =A0 =A0 =A0if (p2 =3D=3D NULL) {
+ =A0 =A0 =A0 =A0 =A0= =A0len =3D strlen(p);
+ =A0 =A0 =A0 =A0} else {
+ =A0 = =A0 =A0 =A0 =A0 =A0len =3D p2 - p;
+ =A0 =A0 =A0 =A0}
+=
+ =A0 =A0 =A0 =A0do {
+ =A0 =A0 =A0 =A0 =A0 =A0if (len= <=3D 0)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
+
+ =A0 = =A0 =A0 =A0 =A0 =A0snprintf(temp, sizeof(temp), "%.*s/%s", len, p= , filename);
+
+ =A0 =A0 =A0 =A0 =A0 =A0if (path_exists= (temp) && path_can_exec(temp)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return strdup(temp);
+ =A0 = =A0 =A0 =A0 =A0 =A0}
+
+ =A0 =A0 =A0 =A0} while (0);
+
+ =A0 =A0 =A0 =A0p +=3D len;
+ =A0 =A0 =A0 = =A0if (*p =3D=3D DIR_SEP)
+ =A0 =A0 =A0 =A0 =A0 =A0p++;
+ =A0 =A0}
+
+ =A0 =A0/* Nothing, really */
<= div>+ =A0 =A0return NULL;
+}
diff --git a/hw/goldfish_d= evice.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 *go= ldfish_int_create(GoldfishBus *gbus, uint32_t base, qemu_irq pare
=A0DeviceState *goldfish_timer_create(GoldfishBus *gbus, uint32_t base, in= t irq);
=A0DeviceState *goldfish_rtc_create(GoldfishBus *gbus);
=A0D= eviceState *goldfish_tty_create(GoldfishBus *gbus, CharDriverState *cs, int= id, uint32_t base, int irq);
+DeviceState *goldfish_nand_create(= GoldfishBus *gbus);
=A0
=A0/* Global functions provided by Goldfish devices */
=A0void goldfish_bus_register_withprop(GoldfishDeviceInfo *info);<= /div>
@@ -53,4 +54,208 @@ int goldfish_add_device_no_io(GoldfishDevice = *dev);
=A0void goldfish_device_init(DeviceState *dev, uint32_t base, uint32_t= irq);
=A0void goldfish_device_set_irq(GoldfishDevice *dev, int i= rq, int level);
=A0
+/** TEMP FILE SUPPORT
+ = **
+ ** simple interface to create an empty temporary file on the system.=
+ **
+ ** create the file with tempfile_create(), whic= h 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 ow= ned 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 =A0 TempFil= e;
+
+extern =A0TempFile* =A0 =A0tempfile_create( void );
=
+extern =A0const char* =A0tempfile_path( TempFile* =A0temp );
+extern =A0void =A0 =A0 =A0 =A0 tempfile_close( TempFile* =A0temp );
+
+/** TEMP FILE CLEANUP
+ **
+ ** We delete all temporary files in atexit()-register= ed callbacks.
+ ** however, the Win32 DeleteFile is unable to rem= ove a file unless
+ ** all HANDLEs to it are closed in the termin= ating process.
+ **
+ ** Call 'atexit_close_fd' on a newly open-ed = file descriptor to indicate
+ ** that you want it closed in atexi= t() time. You should always call
+ ** this function unless you= 9;re certain that the corresponding file
+ ** cannot be temporary.
+ **
+ ** Call 'atex= it_close_fd_remove' before explicitely closing a 'fd'
+ **/
+extern void =A0 =A0 =A0 =A0 =A0atexit_close_fd(int =A0fd)= ;
+extern void =A0 =A0 =A0 =A0 =A0atexit_close_fd_remove(int =A0fd);
+
+/** MISC FILE AND DIRECTORY HANDLING
+ **/
+
+/* O_BINARY is required in the MS C library to avoid openin= g file
+ * in text mode (the default, ahhhhh)
+ */
+#if != defined(_WIN32) && !defined(O_BINARY)
+# =A0define =A0O_B= INARY =A00
+#endif
+
+/* define =A0PATH_SEP a= s a string containing the directory separateor */
+#ifdef _WIN32
+# =A0define =A0PATH_SEP =A0 "\\"
+# =A0define =A0PATH_SEP_C '\\'
+#else
+# =A0define =A0PATH_SEP =A0 "/"
+# =A0define =A0PATH_= SEP_C '/'
+#endif
+
+/* get MAX_PATH, note that PATH_MAX is = set to 260 on Windows for
+ * stupid backwards-compatibility reas= on, though any 32-bit version
+ * of the OS handles much much lon= ger paths
+ */
+#ifdef _WIN32
+# =A0undef =A0 MAX_PATH
=
+# =A0define =A0MAX_PATH =A0 =A01024
+# =A0undef =A0 PATH_MA= X
+# =A0define =A0PATH_MAX =A0 =A0MAX_PATH
+#else
=
+# =A0include <limits.h>
+# =A0define =A0MAX_PATH =A0 =A0PATH_MAX
+#endif
+=
+typedef int ABool;
+typedef int APosixStatus;
+
+/* checks that a given file exists */
+extern ABo= ol =A0path_exists( const char* =A0path );
+
+/* checks that a path points to a regular file */
+extern ABool =A0path_is_regular( const char* =A0path );
+
+/* checks that a path points to a directory */
+extern AB= ool =A0path_is_dir( const char* =A0path );
+
+/* checks that a path is absolute or not */
+//= extern ABool =A0path_is_absolute( const char* =A0path );
+
<= div>+/* checks that one can read/write a given (regular) file */
+extern ABool =A0path_can_read( const char* =A0path );
+extern AB= ool =A0path_can_write( const char* =A0path );
+
+/* che= cks that one can execute a given file */
+extern ABool =A0path_ca= n_exec( const char* path );
+
+/* try to make a directory */
+extern APosixSta= tus =A0 path_mkdir( const char* =A0path, int =A0mode );
+
+/* ensure that a given directory exists, create it if not,
+ = =A0 0 on success, -1 on error */
+extern APosixStatus =A0 path_mkdir_if_needed( const char* =A0path, in= t =A0mode );
+
+/* return the size of a given file in &= #39;*psize'. returns 0 on
+ * success, -1 on failure (error c= ode in errno) */
+extern APosixStatus =A0 path_get_size( const char* =A0path, uint64_t = =A0*psize );
+
+/* =A0path_parent() can be used to retu= rn the n-level parent of a given directory
+ * =A0this understand= s . and .. when encountered in the input path.
+ *
+ * =A0the returned string must be freed by the caller.<= /div>
+ */
+extern char* =A0path_parent( const char* =A0path,= int =A0levels );
+
+/* split a path into a (dirname,ba= sename) pair. the result strings must be freed
+ * by the caller. Return 0 on success, or -1 on error. Error conditio= ns include
+ * the following:
+ * =A0 - 'path' = is empty
+ * =A0 - 'path' is "/" or degenerate = cases like "////"
+ * =A0 - basename is "." or ".."
+ *
+ * if there is no directory separator in path, *dirname will be se= t to "."
+ * if the path is of type "/foo", t= hen *dirname will be set to "/"
+ *
+ * pdirname can be NULL if you don't want the direc= tory name
+ * pbasename can be NULL if you don't want the bas= e name
+ */
+extern int =A0 =A0path_split( const char* = =A0path, char* *pdirname, char* *pbasename );
+
+/* a convenience function to retrieve the directory name = as returned by
+ * path_split(). Returns NULL if path_split() ret= urns an error.
+ * the result string must be freed by the caller<= /div>
+ */
+extern char* =A0path_dirname( const char* =A0path );
+
+/* a convenience function to retrieve the base name a= s returned by
+ * path_split(). Returns NULL if path_split() retu= rns an error.
+ * the result must be freed by the caller.
+ */
+= extern char* =A0path_basename( const char* =A0path );
+
+/* look for a given executable in the system path and return its full pat= h.
+ * Returns NULL if not found. Note that on Windows this doesn't n= ot append
+ * an .exe prefix, or other magical thing like Cygwin = usually does.
+ */
+extern char* =A0path_search_exec( c= onst char* filename );
+
+/** OTHER FILE UTILITIES
+ **
+ ** = =A0path_empty_file() creates an empty file at a given path location.
<= div>+ ** =A0if the file already exists, it is truncated without warning
+ **
+ ** =A0path_copy_file() copies one file into another.<= /div>
+ **
+ ** =A0unlink_file() is equivalent to unlink() on= Unix, on Windows,
+ ** =A0it will handle the case where _unlink(= ) fails because the file is
+ ** =A0read-only by trying to change its access rights then calling _= unlink()
+ ** =A0again.
+ **
+ ** =A0these fu= nctions return 0 on success, and -1 on error
+ **
+ ** = =A0load_text_file() reads a file into a heap-allocated memory block,
+ ** =A0and appends a 0 to it. the caller must free it
+ **/=
+
+/* creates an empty file at a given location. If th= e file already
+ * exists, it is truncated without warning. retur= ns 0 on success,
+ * or -1 on failure.
+ */
+extern APosixStatus = =A0 path_empty_file( const char* =A0path );
+
+/* copie= s on file into another one. 0 on success, -1 on failure
+ * (erro= r code in errno). Does not work on directories */
+extern APosixStatus =A0 path_copy_file( const char* =A0dest, const ch= ar* =A0source );
+
+/* unlink/delete a given file. Note= that on Win32, this will
+ * fail if the program has an opened h= andle to the file
+ */
+extern APosixStatus =A0 path_delete_file( const char* = =A0path );
+
+/* try to load a given file into a heap-a= llocated 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.<= /div>
+ * In case of failure, NULL is returned and the error code is in= errno
+ */
+extern void* =A0 =A0 =A0 =A0 =A0path_load_= file( const char* =A0path, size_t =A0*pSize );
+
+/** FILE LOCKS SUPPORT
+ **
+ ** a Fi= leLock is useful to prevent several emulator instances from using the same<= /div>
+ ** writable file (e.g. the userdata.img disk images).
+ **
+ ** create a FileLock object with filelock_create(), t= he 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-exist= ing path and that the lock creation
+ ** function can detect stal= e file locks that can longer when the emulator
+ ** crashes unexp= ectedly, 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 =A0FileLock;
+
+extern FileLoc= k* =A0filelock_create ( const char* =A0path );
+extern void =A0 = =A0 =A0 filelock_release( FileLock* =A0lock );
+
=A0#en= dif
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c
new file mod= e 100644
index 0000000..f540bda
--- /dev/null
+++ b/hw/goldfish_nand.c
@@ -0,0 +1,914 @@
+/* Copyrig= ht (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 S= oftware Foundation, and
+** may be copied, distributed, and modif= ied under those terms.
+**
+** This program is distributed in the hope that it will= be useful,
+** but WITHOUT ANY WARRANTY; without even the implie= d warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PUR= POSE. =A0See the
+** GNU General Public License for more details.
+*/
+#include "hw.h"
+#include "goldfish_device.h&q= uot;
+#include "goldfish_nand_reg.h"
+#includ= e "goldfish_nand.h"
+
+#ifdef TARGET_I386
+#include "kvm.h"<= /div>
+#endif
+
+#define =A0DEBUG =A00
+#= if DEBUG
+# =A0define =A0D(...) =A0 =A0VERBOSE_PRINT(init,__VA_AR= GS__)
+# =A0define =A0D_ACTIVE =A0VERBOSE_CHECK(init)
+# =A0define= =A0T(...) =A0 =A0VERBOSE_PRINT(nand_limits,__VA_ARGS__)
+# =A0de= fine =A0T_ACTIVE =A0VERBOSE_CHECK(nand_limits)
+#else
+= # =A0define =A0D(...) =A0 =A0((void)0)
+# =A0define =A0D_ACTIVE =A00
+# =A0define =A0T(...) =A0 =A0= ((void)0)
+# =A0define =A0T_ACTIVE =A00
+#endif
+#define =A0PANIC(...) do { fprintf(stderr, __VA_ARGS__); =A0\
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 exit(1); =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 \
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0} while (0)
+
=
+/* lseek uses 64-bit offsets on Darwin. */
+/* prefer lseek= 64 on Linux =A0 =A0 =A0 =A0 =A0 =A0 =A0*/
+#ifdef __APPLE__
=
+# =A0define =A0llseek =A0lseek
+#elif defined(__linux__)
+# =A0define =A0llseek =A0lseek64<= /div>
+#endif
+
+#define =A0XLOG =A0xlog
= +
+static void
+xlog( const char* =A0format, ... )
+{
+ =A0 =A0/*
+ =A0 =A0va_list =A0args;
+ =A0 =A0va_= start(args, format);
+ =A0 =A0fprintf(stderr, "NAND: ")= ;
+ =A0 =A0vfprintf(stderr, format, args);
+ =A0 =A0va_= end(args);
+ =A0 =A0*/
+}
+
+/* Information on a single device/nand image= used by the emulator
+ */
+typedef struct {
= + =A0 =A0char* =A0 =A0 =A0devname; =A0 =A0 =A0/* name for this device (not = null-terminated, use len below) */
+ =A0 =A0size_t =A0 =A0 devname_len;
+ =A0 =A0uint8_t* =A0 d= ata; =A0 =A0 =A0 =A0 /* buffer for read/write actions to underlying image *= /
+ =A0 =A0int =A0 =A0 =A0 =A0fd;
+ =A0 =A0uint32_t =A0= flags;
+ =A0 =A0uint32_t =A0 page_size;
+ =A0 =A0uint32_t =A0 extra_size;
+ =A0 =A0uint32_t =A0 eras= e_size; =A0 /* size of the data buffer mentioned above */
+ =A0 = =A0uint64_t =A0 max_size; =A0 =A0 /* Capacity limit for the image. The actu= al underlying
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* file may be = smaller. */
+} nand_dev;
+
+nand_threshold = =A0 =A0android_nand_write_threshold;
+nand_threshold =A0 =A0andro= id_nand_read_threshold;
+
+#ifdef CONFIG_NAND_THRESHOLD
+
+/* update a thres= hold, return 1 if limit is hit, 0 otherwise */
+static void
=
+nand_threshold_update( nand_threshold* =A0t, uint32_t =A0len )
+{
+ =A0 =A0if (t->counter < t->limit) {
+ =A0= =A0 =A0 =A0uint64_t =A0avail =3D t->limit - t->counter;
+ = =A0 =A0 =A0 =A0if (avail > len)
+ =A0 =A0 =A0 =A0 =A0 =A0avail= =3D len;
+
+ =A0 =A0 =A0 =A0if (t->counter =3D=3D 0= ) {
+ =A0 =A0 =A0 =A0 =A0 =A0T("%s: starting threshold counting to %l= ld",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0__FUNCTION__, t->limit)= ;
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0t->counter += =3D avail;
+ =A0 =A0 =A0 =A0if (t->counter >=3D t->limit= ) {
+ =A0 =A0 =A0 =A0 =A0 =A0/* threshold reach, send a signal to an exter= nal process */
+ =A0 =A0 =A0 =A0 =A0 =A0T( "%s: sending sign= al %d to pid %d !",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 __FUNCTION= __, t->signal, t->pid );
+
+ =A0 =A0 =A0 =A0 =A0 =A0kill( t->pid, t->signal );<= /div>
+ =A0 =A0 =A0 =A0}
+ =A0 =A0}
+ =A0 =A0return= ;
+}
+
+#define =A0NAND_UPDATE_READ_THRESHOLD= (len) =A0\
+ =A0 =A0nand_threshold_update( &android_nand_read= _threshold, (uint32_t)(len) )
+
+#define =A0NAND_UPDATE_WRITE_THRESHOLD(len) =A0\
+ =A0 =A0nand_threshold_update( &android_nand_write_threshold, (uint3= 2_t)(len) )
+
+#else /* !NAND_THRESHOLD */
+<= /div>
+#define =A0NAND_UPDATE_READ_THRESHOLD(len) =A0\
+ =A0 =A0do= {} while (0)
+
+#define =A0NAND_UPDATE_WRITE_THRESHOLD= (len) =A0\
+ =A0 =A0do {} while (0)
+
+#endif= /* !NAND_THRESHOLD */
+
+static nand_dev *nand_devs =3D NULL;
+static ui= nt32_t nand_dev_count =3D 0;
+
+/* The controller is th= e single access point for all NAND images currently
+ * attached = to the system.
+ */
+typedef struct {
+ =A0 =A0uint32_t base;
+
+ =A0 =A0// register state
+ =A0 =A0uint32_t d= ev; =A0 =A0 =A0 =A0 =A0 =A0/* offset in nand_devs for the device that is
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0* curr= ently being accessed */
+ =A0 =A0uint32_t addr_low;
+ =A0 =A0uint32_t addr_high;
+ =A0 =A0uint32_t transfer_size;
+ =A0 =A0uint32_t data;
+ =A0 =A0uint32_t result;
+} nand_dev_controller_state;<= /div>
+
+typedef struct GoldfishNandDevice {
+ =A0 =A0GoldfishDevice= qdev;
+ =A0 =A0char *system_path;
+ =A0 =A0char *syste= m_init_path;
+ =A0 =A0uint64_t system_size;
+
+ =A0 =A0char *user_data_path;
+ =A0 =A0char *user_data_init_path;
+ =A0 =A0uint64_t user_d= ata_size;
+ =A0 =A0
+ =A0 =A0char *cache_path;
+ =A0 =A0uint64_t cache_size;
+ =A0 =A0uint32_t base;
+
+ =A0 =A0// register state
+ =A0 =A0uint32_t dev; =A0 =A0 =A0 =A0 =A0 =A0/* offset in nand_devs f= or the device that is
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0* currently being accessed */
+ =A0 =A0uint32_= t addr_low;
+ =A0 =A0uint32_t addr_high;
+ =A0 =A0uint32_t transfer_size;
+ =A0 =A0uint32_t data;
+ =A0 =A0uint32_t result;
+} GoldfishNandDevice;
+
+/* EINTR-proof read - due to SIGALRM in use elsewhere */
+static int =A0do_read(int =A0fd, void* =A0buf, size_t =A0size)
+{
+ =A0 =A0int =A0ret;
+ =A0 =A0do {
+ = =A0 =A0 =A0 =A0ret =3D read(fd, buf, size);
+ =A0 =A0} while (ret= < 0 && errno =3D=3D EINTR);
+
+ =A0 =A0retu= rn ret;
+}
+
+/* EINTR-proof write - due to SIGALRM in use elsewhere */
+static int =A0do_write(int =A0fd, const void* =A0buf, size_t =A0size= )
+{
+ =A0 =A0int =A0ret;
+ =A0 =A0do {
=
+ =A0 =A0 =A0 =A0ret =3D write(fd, buf, size);
+ =A0 =A0} while (ret < 0 && errno =3D=3D EINTR);
+
+ =A0 =A0return ret;
+}
+
+/* EI= NTR-proof lseek - due to SIGALRM in use elsewhere */
+static int = =A0do_lseek(int =A0fd, off_t offset, int whence)
+{
+ =A0 =A0int =A0ret;
+ =A0 =A0do {
+ = =A0 =A0 =A0 =A0ret =3D lseek(fd, offset, whence);
+ =A0 =A0} whil= e (ret < 0 && errno =3D=3D EINTR);
+
+ =A0 = =A0return ret;
+}
+
+static uint32_t nand_dev_read_file(nand_dev *dev, uint32_= t data, uint64_t addr, uint32_t total_len)
+{
+ =A0 =A0= uint32_t len =3D total_len;
+ =A0 =A0size_t read_len =3D dev->= erase_size;
+ =A0 =A0int eof =3D 0;
+
+ =A0 =A0NAND_UPDATE_REA= D_THRESHOLD(total_len);
+
+ =A0 =A0do_lseek(dev->fd,= addr, SEEK_SET);
+ =A0 =A0while(len > 0) {
+ =A0 = =A0 =A0 =A0if(read_len < dev->erase_size) {
+ =A0 =A0 =A0 =A0 =A0 =A0memset(dev->data, 0xff, dev->erase_size= );
+ =A0 =A0 =A0 =A0 =A0 =A0read_len =3D dev->erase_size;
+ =A0 =A0 =A0 =A0 =A0 =A0eof =3D 1;
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0if(len < read_len)
+ =A0 =A0 =A0 =A0 =A0 =A0read_len =3D len;
+ =A0 =A0 =A0 =A0if(!e= of) {
+ =A0 =A0 =A0 =A0 =A0 =A0read_len =3D do_read(dev->fd, d= ev->data, read_len);
+ =A0 =A0 =A0 =A0}
+#ifdef TARG= ET_I386
+ =A0 =A0 =A0 =A0if (kvm_enabled())
+ =A0 =A0 =A0 =A0 =A0 =A0cpu_synchronize_state(cpu_single_env, 0);
+#endif
+ =A0 =A0 =A0 =A0cpu_memory_rw_debug(cpu_single_en= v, data, dev->data, read_len, 1);
+ =A0 =A0 =A0 =A0data +=3D r= ead_len;
+ =A0 =A0 =A0 =A0len -=3D read_len;
+ =A0 =A0}
+ =A0 =A0return total_len;
+}
+
+static uint32_t nand_dev_write_file(nand_dev *dev, uint32_t d= ata, uint64_t addr, uint32_t total_len)
+{
+ =A0 =A0uin= t32_t len =3D total_len;
+ =A0 =A0size_t write_len =3D dev->erase_size;
+ =A0 =A0i= nt ret;
+
+ =A0 =A0NAND_UPDATE_WRITE_THRESHOLD(total_le= n);
+
+ =A0 =A0do_lseek(dev->fd, addr, SEEK_SET);
+ =A0 =A0while(len > 0) {
+ =A0 =A0 =A0 =A0if(len < write_len)
+ =A0 =A0 =A0 =A0 = =A0 =A0write_len =3D len;
+#ifdef TARGET_I386
+ =A0 =A0= =A0 =A0if (kvm_enabled())
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0cpu_s= ynchronize_state(cpu_single_env, 0);
+#endif
+ =A0 =A0 =A0 =A0cpu_memory_rw_debug(cpu_single_env, data= , dev->data, write_len, 0);
+ =A0 =A0 =A0 =A0ret =3D do_write(= dev->fd, dev->data, write_len);
+ =A0 =A0 =A0 =A0if(ret <= ; write_len) {
+ =A0 =A0 =A0 =A0 =A0 =A0XLOG("nand_dev_write_file, write failed: %s\n= ", strerror(errno));
+ =A0 =A0 =A0 =A0 =A0 =A0break;
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0data +=3D write_len;
=
+ =A0 =A0 =A0 =A0len -=3D write_len;
+ =A0 =A0}
+ =A0 =A0return total_len - len;
+}
+
+static uint32_t nand_dev_erase_file(nand_dev *dev, uint64_t = addr, uint32_t total_len)
+{
+ =A0 =A0uint32_t len =3D = total_len;
+ =A0 =A0size_t write_len =3D dev->erase_size;
+ =A0 =A0i= nt ret;
+
+ =A0 =A0do_lseek(dev->fd, addr, SEEK_SET)= ;
+ =A0 =A0memset(dev->data, 0xff, dev->erase_size);
<= div>+ =A0 =A0while(len > 0) {
+ =A0 =A0 =A0 =A0if(len < write_len)
+ =A0 =A0 =A0 =A0 = =A0 =A0write_len =3D len;
+ =A0 =A0 =A0 =A0ret =3D do_write(dev-&= gt;fd, dev->data, write_len);
+ =A0 =A0 =A0 =A0if(ret < wri= te_len) {
+ =A0 =A0 =A0 =A0 =A0 =A0XLOG( "nand_dev_write_fil= e, write failed: %s\n", strerror(errno));
+ =A0 =A0 =A0 =A0 =A0 =A0break;
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0len -=3D write_len;
+ =A0 =A0}
+ =A0 = =A0return 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 f= unction as static on Linux-x86, the
+ * emulated kernel will also panic !?
+ *
+ * I s= till wonder if this is a compiler bug, or due to some nasty thing the
=
+ * emulator does with CPU registers during execution of the translate= d code.
+ */
+#if !(defined __APPLE__ && defined __powerpc__= )
+static
+#endif
+uint32_t nand_dev_do_cmd(n= and_dev_controller_state *s, uint32_t cmd)
+{
+ =A0 =A0= uint32_t size;
+ =A0 =A0uint64_t addr;
+ =A0 =A0nand_dev *dev;
+<= /div>
+ =A0 =A0addr =3D s->addr_low | ((uint64_t)s->addr_high <= ;< 32);
+ =A0 =A0size =3D s->transfer_size;
+ =A0= =A0if(s->dev >=3D nand_dev_count)
+ =A0 =A0 =A0 =A0return 0;
+ =A0 =A0dev =3D nand_devs + s-&g= t;dev;
+
+ =A0 =A0switch(cmd) {
+ =A0 =A0case= NAND_CMD_GET_DEV_NAME:
+ =A0 =A0 =A0 =A0if(size > dev->dev= name_len)
+ =A0 =A0 =A0 =A0 =A0 =A0size =3D dev->devname_len;<= /div>
+#ifdef TARGET_I386
+ =A0 =A0 =A0 =A0if (kvm_enabled())
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0cpu_synchronize_state(cpu_single_env= , 0);
+#endif
+ =A0 =A0 =A0 =A0cpu_memory_rw_debug(cpu_= single_env, s->data, (uint8_t*)dev->devname, size, 1);
+ =A0 =A0 =A0 =A0return size;
+ =A0 =A0case NAND_CMD_READ:
+ =A0 =A0 =A0 =A0if(addr >=3D dev->max_size)
+ =A0= =A0 =A0 =A0 =A0 =A0return 0;
+ =A0 =A0 =A0 =A0if(size > dev-&= gt;max_size - addr)
+ =A0 =A0 =A0 =A0 =A0 =A0size =3D dev->max= _size - addr;
+ =A0 =A0 =A0 =A0if(dev->fd >=3D 0)
+ =A0 =A0 =A0 =A0 = =A0 =A0return nand_dev_read_file(dev, s->data, addr, size);
+#= ifdef TARGET_I386
+ =A0 =A0 =A0 =A0if (kvm_enabled())
+= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0cpu_synchronize_state(cpu_single_env, 0);
+#endif
+ =A0 =A0 =A0 =A0cpu_memory_rw_debug(cpu_single_env,= s->data, &dev->data[addr], size, 1);
+ =A0 =A0 =A0 =A0r= eturn size;
+ =A0 =A0case NAND_CMD_WRITE:
+ =A0 =A0 =A0= =A0if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+ =A0 =A0 =A0 =A0 =A0 =A0return 0;
+ =A0 =A0 =A0 =A0if(addr = >=3D dev->max_size)
+ =A0 =A0 =A0 =A0 =A0 =A0return 0;
+ =A0 =A0 =A0 =A0if(size > dev->max_size - addr)
+ = =A0 =A0 =A0 =A0 =A0 =A0size =3D dev->max_size - addr;
+ =A0 =A0 =A0 =A0if(dev->fd >=3D 0)
+ =A0 =A0 =A0 =A0 = =A0 =A0return nand_dev_write_file(dev, s->data, addr, size);
+= #ifdef TARGET_I386
+ =A0 =A0 =A0 =A0if (kvm_enabled())
= + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0cpu_synchronize_state(cpu_single_env, 0);<= /div>
+#endif
+ =A0 =A0 =A0 =A0cpu_memory_rw_debug(cpu_single_env,= s->data, &dev->data[addr], size, 0);
+ =A0 =A0 =A0 =A0r= eturn size;
+ =A0 =A0case NAND_CMD_ERASE:
+ =A0 =A0 =A0= =A0if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+ =A0 =A0 =A0 =A0 =A0 =A0return 0;
+ =A0 =A0 =A0 =A0if(addr = >=3D dev->max_size)
+ =A0 =A0 =A0 =A0 =A0 =A0return 0;
+ =A0 =A0 =A0 =A0if(size > dev->max_size - addr)
+ = =A0 =A0 =A0 =A0 =A0 =A0size =3D dev->max_size - addr;
+ =A0 =A0 =A0 =A0if(dev->fd >=3D 0)
+ =A0 =A0 =A0 =A0 = =A0 =A0return nand_dev_erase_file(dev, addr, size);
+ =A0 =A0 =A0= =A0memset(&dev->data[addr], 0xff, size);
+ =A0 =A0 =A0 = =A0return size;
+ =A0 =A0case NAND_CMD_BLOCK_BAD_GET: // no bad b= lock support
+ =A0 =A0 =A0 =A0return 0;
+ =A0 =A0case NAND_CMD_BLOCK_BAD_= SET:
+ =A0 =A0 =A0 =A0if(dev->flags & NAND_DEV_FLAG_READ_O= NLY)
+ =A0 =A0 =A0 =A0 =A0 =A0return 0;
+ =A0 =A0 =A0 = =A0return 0;
+ =A0 =A0default:
+ =A0 =A0 =A0 =A0cpu_abort(cpu_single_env, "nand_dev_do_cmd: Bad = command %x\n", cmd);
+ =A0 =A0 =A0 =A0return 0;
+ = =A0 =A0}
+}
+
+/* I/O write */
+sta= tic void nand_dev_write(void *opaque, target_phys_addr_t offset, uint32_t v= alue)
+{
+ =A0 =A0nand_dev_controller_state *s =3D (nand_dev_contr= oller_state *)opaque;
+
+ =A0 =A0switch (offset) {
+ =A0 =A0case NAND_DEV:
+ =A0 =A0 =A0 =A0s->dev =3D valu= e;
+ =A0 =A0 =A0 =A0if(s->dev >=3D nand_dev_count) {
+ =A0 =A0 =A0 =A0 =A0 =A0cpu_abort(cpu_single_env, "nand_dev_writ= e: Bad dev %x\n", value);
+ =A0 =A0 =A0 =A0}
+ =A0= =A0 =A0 =A0break;
+ =A0 =A0case NAND_ADDR_HIGH:
+ =A0 = =A0 =A0 =A0s->addr_high =3D value;
+ =A0 =A0 =A0 =A0break;
+ =A0 =A0case NAND_ADDR_LOW:
+ =A0 =A0 =A0 =A0s->addr_low =3D value;
+ =A0 =A0 =A0 =A0br= eak;
+ =A0 =A0case NAND_TRANSFER_SIZE:
+ =A0 =A0 =A0 = =A0s->transfer_size =3D value;
+ =A0 =A0 =A0 =A0break;
+ =A0 =A0case NAND_DATA:
+ =A0 = =A0 =A0 =A0s->data =3D value;
+ =A0 =A0 =A0 =A0break;
+ =A0 =A0case NAND_COMMAND:
+ =A0 =A0 =A0 =A0s->result =3D n= and_dev_do_cmd(s, value);
+ =A0 =A0 =A0 =A0break;
+ =A0 =A0default:
+ =A0 =A0 =A0 =A0cpu_abort(cpu_single_env,= "nand_dev_write: Bad offset %x\n", offset);
+ =A0 =A0 = =A0 =A0break;
+ =A0 =A0}
+}
+
+/* I= /O read */
+static uint32_t nand_dev_read(void *opaque, target_phys_addr_t offset)
+{
+ =A0 =A0nand_dev_controller_state *s =3D (nand_dev_con= troller_state *)opaque;
+ =A0 =A0nand_dev *dev;
+
=
+ =A0 =A0switch (offset) {
+ =A0 =A0case NAND_VERSION:
+ =A0 =A0 =A0 =A0return NAND_VER= SION_CURRENT;
+ =A0 =A0case NAND_NUM_DEV:
+ =A0 =A0 =A0= =A0return nand_dev_count;
+ =A0 =A0case NAND_RESULT:
+= =A0 =A0 =A0 =A0return s->result;
+ =A0 =A0}
+
+ =A0 =A0if(s->dev >=3D nand_de= v_count)
+ =A0 =A0 =A0 =A0return 0;
+
+ =A0 = =A0dev =3D nand_devs + s->dev;
+
+ =A0 =A0switch (of= fset) {
+ =A0 =A0case NAND_DEV_FLAGS:
+ =A0 =A0 =A0 =A0return dev->flags;
+
+ =A0 =A0= case NAND_DEV_NAME_LEN:
+ =A0 =A0 =A0 =A0return dev->devname_l= en;
+
+ =A0 =A0case NAND_DEV_PAGE_SIZE:
+ =A0= =A0 =A0 =A0return dev->page_size;
+
+ =A0 =A0case NAND_DEV_EXTRA_SIZE:
+ =A0 =A0 =A0= =A0return dev->extra_size;
+
+ =A0 =A0case NAND_DEV= _ERASE_SIZE:
+ =A0 =A0 =A0 =A0return dev->erase_size;
+
+ =A0 =A0case NAND_DEV_SIZE_LOW:
+ =A0 =A0 =A0 =A0return (uint32_t)dev->max_size;
+
<= div>+ =A0 =A0case NAND_DEV_SIZE_HIGH:
+ =A0 =A0 =A0 =A0return (ui= nt32_t)(dev->max_size >> 32);
+
+ =A0 =A0defau= lt:
+ =A0 =A0 =A0 =A0cpu_abort(cpu_single_env, "nand_dev_rea= d: Bad offset %x\n", offset);
+ =A0 =A0 =A0 =A0return 0;
+ =A0 =A0}
+}
+
+static CPUReadMemoryFunc *nand_dev_readfn[] =3D {
+= =A0 nand_dev_read,
+ =A0 nand_dev_read,
+ =A0 nand_dev= _read
+};
+
+static CPUWriteMemoryFunc *nand_dev_writefn[] = =3D {
+ =A0 nand_dev_write,
+ =A0 nand_dev_write,
=
+ =A0 nand_dev_write
+};
+
+/* initializ= e the QFB device */
+static int arg_match(const char *a, const char *b, size_t b_len)
+{
+ =A0 =A0while(*a && b_len--) {
+ =A0 = =A0 =A0 =A0if(*a++ !=3D *b++)
+ =A0 =A0 =A0 =A0 =A0 =A0return 0;<= /div>
+ =A0 =A0}
+ =A0 =A0return b_len =3D=3D 0;
+}
+
+vo= id nand_add_dev(const char *arg)
+{
+ =A0 =A0uint64_t d= ev_size =3D 0;
+ =A0 =A0const char *next_arg;
+ =A0 =A0= const char *value;
+ =A0 =A0size_t arg_len, value_len;
+ =A0 =A0nand_dev *new_d= evs, *dev;
+ =A0 =A0char *devname =3D NULL;
+ =A0 =A0si= ze_t devname_len =3D 0;
+ =A0 =A0char *initfilename =3D NULL;
+ =A0 =A0char *rwfilename =3D NULL;
+ =A0 =A0int initfd =3D -1;
+ =A0 =A0int rwfd =3D -1;
<= div>+ =A0 =A0int read_only =3D 0;
+ =A0 =A0int pad;
+ = =A0 =A0ssize_t read_size;
+ =A0 =A0uint32_t page_size =3D 2048;
+ =A0 =A0uint32_t extra_size =3D 64;
+ =A0 =A0uint32_t erase_pages =3D 64;
+
+ =A0 =A0/= /VERBOSE_PRINT(init, "%s: %s", __FUNCTION__, arg);
+
+ =A0 =A0while(arg) {
+ =A0 =A0 =A0 =A0next_arg =3D strch= r(arg, ',');
+ =A0 =A0 =A0 =A0value =3D strchr(arg, '=3D');
+ =A0= =A0 =A0 =A0if(next_arg !=3D NULL) {
+ =A0 =A0 =A0 =A0 =A0 =A0arg= _len =3D next_arg - arg;
+ =A0 =A0 =A0 =A0 =A0 =A0next_arg++;
+ =A0 =A0 =A0 =A0 =A0 =A0if(value >=3D next_arg)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0value =3D NULL;
+ =A0 =A0 = =A0 =A0}
+ =A0 =A0 =A0 =A0else
+ =A0 =A0 =A0 =A0 =A0 = =A0arg_len =3D strlen(arg);
+ =A0 =A0 =A0 =A0if(value !=3D NULL) = {
+ =A0 =A0 =A0 =A0 =A0 =A0size_t new_arg_len =3D value - arg;
+ =A0 =A0 =A0 =A0 =A0 =A0value_len =3D arg_len - new_arg_len - 1;
+ =A0 =A0 =A0 =A0 =A0 =A0arg_len =3D new_arg_len;
+ =A0 =A0= =A0 =A0 =A0 =A0value++;
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 = =A0 =A0else
+ =A0 =A0 =A0 =A0 =A0 =A0value_len =3D 0;
+
+ =A0 =A0 =A0 =A0if(devname =3D=3D NULL) {
+ =A0= =A0 =A0 =A0 =A0 =A0if(value !=3D NULL)
+ =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0goto bad_arg_and_value;
+ =A0 =A0 =A0 =A0 =A0 =A0devname_= len =3D arg_len;
+ =A0 =A0 =A0 =A0 =A0 =A0devname =3D malloc(arg_= len+1);
+ =A0 =A0 =A0 =A0 =A0 =A0if(devname =3D=3D NULL)
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0goto out_of_memory;
+ =A0 =A0 =A0 =A0 =A0 = =A0memcpy(devname, arg, arg_len);
+ =A0 =A0 =A0 =A0 =A0 =A0devnam= e[arg_len] =3D 0;
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0e= lse if(value =3D=3D NULL) {
+ =A0 =A0 =A0 =A0 =A0 =A0if(arg_match("readonly", arg, arg_l= en)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0read_only =3D 1;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0else {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0XLOG("bad arg: %.*s\n", ar= g_len, arg);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0 = =A0 =A0}
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0else {
+ =A0 =A0 =A0 =A0 =A0 =A0if(arg_match("size", arg, arg_len= )) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0char *ep;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0dev_size =3D strtoull(value, &ep, 0);<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if(ep !=3D value + value_len)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto bad_arg_and_value;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0else = if(arg_match("pagesize", arg, arg_len)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0char *ep;
+ =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0page_size =3D strtoul(value, &ep, 0);
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0if(ep !=3D value + value_len)
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto bad_arg_and_value;
+ =A0 = =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0else if(arg_match("extrasize", arg,= arg_len)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0char *ep;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0extra_size =3D strtoul(value, &ep, 0)= ;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if(ep !=3D value + value_len)<= /div>
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto bad_arg_and_value;
=
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0else if(= arg_match("erasepages", arg, arg_len)) {
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0char *ep;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0era= se_pages =3D strtoul(value, &ep, 0);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if(ep !=3D value + value_len)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto bad_arg_and_value;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0else if(ar= g_match("initfile", arg, arg_len)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0initfilename =3D malloc(value_len + 1);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if(initfilename =3D=3D NULL)
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto out_of_memory;
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0memcpy(initfilename, value, value_len);
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0initfilename[value_len] =3D '\0&= #39;;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0else if(= arg_match("file", arg, arg_len)) {
+ =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0rwfilename =3D malloc(value_len + 1);
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0if(rwfilename =3D=3D NULL)
+ =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0goto out_of_memory;
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0memcpy(rwfilename, value, value_len);=
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rwfilename[value_len] =3D '= \0';
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 = =A0 =A0else {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto bad_arg_and_v= alue;
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0}
+
+ =A0 =A0 =A0 =A0arg =3D next_arg;
+ =A0 =A0}
+=
+ =A0 =A0if (rwfilename =3D=3D NULL) {
+ =A0 =A0 =A0 = =A0/* we create a temporary file to store everything */
+ =A0 =A0 =A0 =A0TempFile* =A0 =A0tmp =3D tempfile_create();
+
+ =A0 =A0 =A0 =A0if (tmp =3D=3D NULL) {
+ =A0 =A0 = =A0 =A0 =A0 =A0XLOG("could not create temp file for %.*s NAND disk ima= ge: %s\n",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0devname_len,= devname, strerror(errno));
+ =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0rwfilename =3D (char*) tempfile_path(tmp);
+ = =A0 =A0 =A0 // if (VERBOSE_CHECK(init))
+ =A0 =A0 =A0 // =A0 =A0 = dprint( "mapping '%.*s' NAND image to %s", devname_len, d= evname, rwfilename);
+ =A0 =A0}
+
+ =A0 =A0if(rwfilename) {
+= =A0 =A0 =A0 =A0rwfd =3D open(rwfilename, O_BINARY | (read_only ? O_RDONLY = : O_RDWR));
+ =A0 =A0 =A0 =A0if(rwfd < 0) {
+ =A0 = =A0 =A0 =A0 =A0 =A0XLOG("could not open file %s, %s\n", rwfilenam= e, strerror(errno));
+ =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0/* this could be a writable temporary file. use atexit_= close_fd to ensure
+ =A0 =A0 =A0 =A0 * that it is properly cleane= d up at exit on Win32
+ =A0 =A0 =A0 =A0 */
+ =A0 =A0 =A0 =A0if (!read_only)
+= =A0 =A0 =A0 =A0 =A0 =A0atexit_close_fd(rwfd);
+ =A0 =A0}
+
+ =A0 =A0if(initfilename) {
+ =A0 =A0 =A0 =A0initf= d =3D open(initfilename, O_BINARY | O_RDONLY);
+ =A0 =A0 =A0 =A0if(initfd < 0) {
+ =A0 =A0 =A0 =A0 =A0 = =A0XLOG("could not open file %s, %s\n", initfilename, strerror(er= rno));
+ =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 = =A0}
+ =A0 =A0 =A0 =A0if(dev_size =3D=3D 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0dev_size =3D do_lseek(initfd, 0, SEEK_END);
+ =A0 =A0 =A0 =A0 =A0 =A0do_lseek(initfd, 0, SEEK_SET);
= + =A0 =A0 =A0 =A0}
+ =A0 =A0}
+
+ =A0 =A0new_= devs =3D realloc(nand_devs, sizeof(nand_devs[0]) * (nand_dev_count + 1));
+ =A0 =A0if(new_devs =3D=3D NULL)
+ =A0 =A0 =A0 =A0goto out_= of_memory;
+ =A0 =A0nand_devs =3D new_devs;
+ =A0 =A0de= v =3D &new_devs[nand_dev_count];
+
+ =A0 =A0dev->= ;page_size =3D page_size;
+ =A0 =A0dev->extra_size =3D extra_size;
+ =A0 =A0dev->= ;erase_size =3D erase_pages * (page_size + extra_size);
+ =A0 =A0= pad =3D dev_size % dev->erase_size;
+ =A0 =A0if (pad !=3D 0) {=
+ =A0 =A0 =A0 =A0dev_size +=3D (dev->erase_size - pad);
+ =A0 =A0 =A0 =A0D("rounding devsize up to a full eraseunit, now = %llx\n", dev_size);
+ =A0 =A0}
+ =A0 =A0dev->de= vname =3D devname;
+ =A0 =A0dev->devname_len =3D devname_len;<= /div>
+ =A0 =A0dev->max_size =3D dev_size;
+ =A0 =A0dev->data =3D malloc(dev->erase_size);
+ =A0 = =A0if(dev->data =3D=3D NULL)
+ =A0 =A0 =A0 =A0goto out_of_memo= ry;
+ =A0 =A0dev->flags =3D read_only ? NAND_DEV_FLAG_READ_ONL= Y : 0;
+
+ =A0 =A0if (initfd >=3D 0) {
+ =A0 =A0 =A0 =A0do {
=
+ =A0 =A0 =A0 =A0 =A0 =A0read_size =3D do_read(initfd, dev->data, d= ev->erase_size);
+ =A0 =A0 =A0 =A0 =A0 =A0if(read_size < 0)= {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0XLOG("could not read fil= e %s, %s\n", initfilename, strerror(errno));
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0 = =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0if(do_write(rwfd, dev->data,= read_size) !=3D read_size) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0XL= OG("could not write file %s, %s\n", rwfilename, strerror(errno));=
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0 = =A0 =A0}
+ =A0 =A0 =A0 =A0} while(read_size =3D=3D dev->erase_= size);
+ =A0 =A0 =A0 =A0close(initfd);
+ =A0 =A0}
=
+ =A0 =A0dev->fd =3D rwfd;
+
+ =A0 =A0nand_dev_count++;
+
+ =A0 =A0return;
+
+out_of_memory:
+ =A0 =A0XLOG("out of memory\n&= quot;);
+ =A0 =A0exit(1);
+
+bad_arg_and_valu= e:
+ =A0 =A0XLOG("bad arg: %.*s=3D%.*s\n", arg_len, arg, value_len, = value);
+ =A0 =A0exit(1);
+}
+
+sta= tic int goldfish_nand_init(GoldfishDevice *dev)
+{
+ = =A0 =A0GoldfishNandDevice *s =3D (GoldfishNandDevice *)dev;
+ =A0 =A0/* Initialize system partition image */
+ =A0 =A0{<= /div>
+ =A0 =A0 =A0 =A0char =A0 =A0 =A0 =A0tmp[PATH_MAX+32];
= + =A0 =A0 =A0 =A0const char* sysImage =3D s->system_path;
+ = =A0 =A0 =A0 =A0const char* initImage =3D s->system_init_path;
+ =A0 =A0 =A0 =A0uint64_t =A0 =A0sysBytes =3D s->system_size;
=
+
+ =A0 =A0 =A0 =A0if (sysBytes =3D=3D 0) {
+ =A0 = =A0 =A0 =A0 =A0 =A0PANIC("Invalid system partition size: %jd", sy= sBytes);
+ =A0 =A0 =A0 =A0}
+
+ =A0 =A0 =A0 =A0snprintf(tmp,sizeof(tmp),"system,siz= e=3D0x%jx", sysBytes);
+
+ =A0 =A0 =A0 =A0if (sysI= mage && *sysImage) {
+ =A0 =A0 =A0 =A0 =A0 =A0if (fileloc= k_create(sysImage) =3D=3D NULL) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf(stderr,"WARNING: System = image already in use, changes will not persist!\n");
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0/* If there is no file=3D parameters, nand_add_d= ev will create
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 * a temporary fi= le to back the partition image. */
+ =A0 =A0 =A0 =A0 =A0 =A0} else {
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0pstrcat(tmp,sizeof(tmp),",file=3D");
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0pstrcat(tmp,sizeof(tmp),sysImage);
+ =A0 = =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 = =A0if (initImage && *initImage) {
+ =A0 =A0 =A0 =A0 =A0 =A0if (!path_exists(initImage)) {
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0PANIC("Invalid initial system image pat= h: %s", initImage);
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ = =A0 =A0 =A0 =A0 =A0 =A0pstrcat(tmp,sizeof(tmp),",initfile=3D");
+ =A0 =A0 =A0 =A0 =A0 =A0pstrcat(tmp,sizeof(tmp),initImage);
+ =A0 =A0 =A0 =A0} else {
+ =A0 =A0 =A0 =A0 =A0 =A0PANIC("M= issing initial system image path!");
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0nand_add_dev(tmp);
+ =A0 =A0}
+
+ =A0 =A0/* Initialize data partition= image */
+ =A0 =A0{
+ =A0 =A0 =A0 =A0char =A0 =A0 =A0 = =A0tmp[PATH_MAX+32];
+ =A0 =A0 =A0 =A0const char* dataImage =3D s= ->user_data_path;
+ =A0 =A0 =A0 =A0const char* initImage =3D s= ->user_data_init_path;
+ =A0 =A0 =A0 =A0uint64_t =A0 =A0dataBytes =3D s->user_data_size;
+
+ =A0 =A0 =A0 =A0if (dataBytes =3D=3D 0) {
+= =A0 =A0 =A0 =A0 =A0 =A0PANIC("Invalid data partition size: %jd",= dataBytes);
+ =A0 =A0 =A0 =A0}
+
+ =A0 =A0 =A0 =A0snprintf(tmp,sizeof(tmp),"userdata,s= ize=3D0x%jx", dataBytes);
+
+ =A0 =A0 =A0 =A0if (d= ataImage && *dataImage) {
+ =A0 =A0 =A0 =A0 =A0 =A0if (fi= lelock_create(dataImage) =3D=3D NULL) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf(stderr, "WARNING: Data p= artition already in use. Changes will not persist!\n");
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Note: if there is no file=3D parameters, = nand_add_dev() will
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 * =A0 =A0 = =A0 create a temporary file to back the partition image. */
+ =A0 =A0 =A0 =A0 =A0 =A0} else {
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0/* Create the file if needed */
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0if (!path_exists(dataImage)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0if (path_empty_file(dataImage) < 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0PANIC("Could not= create data image file %s: %s", dataImage, strerror(errno));
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pstrcat(tmp, si= zeof(tmp), ",file=3D");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pstrcat(tmp, sizeof(tmp), dataImage);=
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0if (initImage && *initImage) {
+ =A0 = =A0 =A0 =A0 =A0 =A0pstrcat(tmp, sizeof(tmp), ",initfile=3D");
+ =A0 =A0 =A0 =A0 =A0 =A0pstrcat(tmp, sizeof(tmp), initImage);
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0nand_add_dev(tmp);
+ =A0 =A0}
+
+ =A0 =A0/* Initialize cache partition = */
+ =A0 =A0{
+ =A0 =A0 =A0 =A0char =A0 =A0 =A0 =A0tmp[= PATH_MAX+32];
+ =A0 =A0 =A0 =A0const char* partPath =3D s->cache_path;
= + =A0 =A0 =A0 =A0uint64_t =A0 =A0partSize =3D s->cache_size;
+=
+ =A0 =A0 =A0 =A0snprintf(tmp,sizeof(tmp),"cache,size=3D0x%= jx", partSize);
+
+ =A0 =A0 =A0 =A0if (partPath && *partPath && s= trcmp(partPath, "<temp>") !=3D 0) {
+ =A0 =A0 =A0= =A0 =A0 =A0if (filelock_create(partPath) =3D=3D NULL) {
+ =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf(stderr, "WARNING: Cache partition a= lready in use. Changes will not persist!\n");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Note: if there is no file=3D param= eters, nand_add_dev() will
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 * = =A0 =A0 =A0 create a temporary file to back the partition image. */
+ =A0 =A0 =A0 =A0 =A0 =A0} else {
+ =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0/* Create the file if needed */
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (!path_exists(partPath)) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (path_empty_file(partPath) &= lt; 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0PANIC(&= quot;Could not create cache image file %s: %s", partPath, strerror(err= no));
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0pstrcat(tmp, s= izeof(tmp), ",file=3D");
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0pstrcat(tmp, sizeof(tmp), partPath);
+ =A0 =A0 =A0 =A0 =A0 =A0= }
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0nand_add_dev(tmp);
=
+ =A0 =A0}
+ =A0 =A0return 0;
+}
+
=
+DeviceState *goldfish_nand_create(GoldfishBus *gbus)
+{
+ =A0 =A0DeviceState *dev;
+ =A0 =A0char *name =3D (char *)"goldfish_nand";
+=
+ =A0 =A0dev =3D qdev_create(&gbus->bus, name);
+ =A0 =A0qdev_prop_set_string(dev, "name", name);
+ = =A0 =A0qdev_init_nofail(dev);
+
+ =A0 =A0return dev;
+}
+
+s= tatic GoldfishDeviceInfo goldfish_nand_info =3D {
+ =A0 =A0.init = =3D goldfish_nand_init,
+ =A0 =A0.readfn =3D nand_dev_readfn,
+ =A0 =A0.writefn =3D nand_dev_writefn,
+ =A0 =A0.qdev.name= =A0=3D "goldfish_nand",
+ =A0 =A0.qdev.size =A0=3D siz= eof(GoldfishNandDevice),
+ =A0 =A0.qdev.props =3D (Property[]) {<= /div>
+ =A0 =A0 =A0 =A0DEFINE_PROP_UINT32("base", GoldfishDev= ice, base, 0),
+ =A0 =A0 =A0 =A0DEFINE_PROP_UINT32("id", GoldfishDevice, id= , 0),
+ =A0 =A0 =A0 =A0DEFINE_PROP_UINT32("size", Goldf= ishDevice, size, 0xfff),
+ =A0 =A0 =A0 =A0DEFINE_PROP_UINT32(&quo= t;irq", GoldfishDevice, irq, 0),
+ =A0 =A0 =A0 =A0DEFINE_PROP_UINT32("irq_count", GoldfishDev= ice, irq_count, 1),
+ =A0 =A0 =A0 =A0DEFINE_PROP_STRING("nam= e", GoldfishDevice, name),
+ =A0 =A0 =A0 =A0DEFINE_PROP_STRI= NG("system_path", GoldfishNandDevice, system_path),
+ =A0 =A0 =A0 =A0DEFINE_PROP_STRING("system_init_path", Gold= fishNandDevice, system_init_path),
+ =A0 =A0 =A0 =A0DEFINE_PROP_U= INT64("system_size", GoldfishNandDevice, system_size, 0x7100000),=
+ =A0 =A0 =A0 =A0DEFINE_PROP_STRING("user_data_path", GoldfishNan= dDevice, user_data_path),
+ =A0 =A0 =A0 =A0DEFINE_PROP_STRING("user_data_init_path", G= oldfishNandDevice, user_data_init_path),
+ =A0 =A0 =A0 =A0DEFINE_= PROP_UINT64("user_data_size", GoldfishNandDevice, user_data_size,= 0x4200000),
+ =A0 =A0 =A0 =A0DEFINE_PROP_STRING("cache_path", GoldfishNa= ndDevice, cache_path),
+ =A0 =A0 =A0 =A0DEFINE_PROP_UINT64("= cache_size", GoldfishNandDevice, cache_size, 0x4200000),
+ = =A0 =A0 =A0 =A0DEFINE_PROP_END_OF_LIST(),
+ =A0 =A0},
+};
+
+static void goldfish_= nand_register(void)
+{
+ =A0 =A0goldfish_bus_register_w= ithprop(&goldfish_nand_info);
+}
+device_init(goldf= ish_nand_register);
+
+#ifdef CONFIG_NAND_LIMITS
+
+static u= int64_t
+parse_nand_rw_limit( const char* =A0value )
+{=
+ =A0 =A0char* =A0 =A0 end;
+ =A0 =A0uint64_t =A0val = =3D strtoul( value, &end, 0 );
+
+ =A0 =A0if (end =3D=3D value) {
+ =A0 =A0 =A0 = =A0derror( "bad parameter value '%s': expecting unsigned integ= er", value );
+ =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0}=
+
+ =A0 =A0switch (end[0]) {
+ =A0 =A0 =A0 =A0case 'K': =A0val <<=3D 10; break;
=
+ =A0 =A0 =A0 =A0case 'M': =A0val <<=3D 20; break;
=
+ =A0 =A0 =A0 =A0case 'G': =A0val <<=3D 30; break;
=
+ =A0 =A0 =A0 =A0case 0: break;
+ =A0 =A0 =A0 =A0default:
+ =A0 =A0 =A0 =A0 =A0 =A0derror( &= quot;bad read/write limit suffix: use K, M or G" );
+ =A0 = =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0}
+ =A0 =A0return = val;
+}
+
+void
+parse_nand_limits(char* =A0limits)
+{
= + =A0 =A0int =A0 =A0 =A0pid =3D -1, signal =3D -1;
+ =A0 =A0int64= _t =A0reads =3D 0, writes =3D 0;
+ =A0 =A0char* =A0 =A0item =3D l= imits;
+
+ =A0 =A0/* parse over comma-separated items *= /
+ =A0 =A0while (item && *item) {
+ =A0 =A0 =A0 =A0ch= ar* =A0next =3D strchr(item, ',');
+ =A0 =A0 =A0 =A0char*= =A0end;
+
+ =A0 =A0 =A0 =A0if (next =3D=3D NULL) {
+ =A0 =A0 =A0 =A0 =A0 =A0next =3D item + strlen(item);
+ =A0 =A0 =A0 =A0} else {
+ =A0 =A0 =A0 =A0 =A0 =A0*next++ = =3D 0;
+ =A0 =A0 =A0 =A0}
+
+ =A0 =A0 =A0 =A0= if ( !memcmp(item, "pid=3D", 4) ) {
+ =A0 =A0 =A0 =A0 = =A0 =A0pid =3D strtol(item+4, &end, 10);
+ =A0 =A0 =A0 =A0 =A0 =A0if (end =3D=3D NULL || *end) {
+ =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0derror( "bad parameter, expecting pid=3D<nu= mber>, got '%s'",
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0item );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exit(= 1);
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0 =A0 =A0if (pid = <=3D 0) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0derror( "bad p= arameter: process identifier must be > 0" );
+ =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0else if ( !memcmp(item, = "signal=3D", 7) ) {
+ =A0 =A0 =A0 =A0 =A0 =A0signal =3D= strtol(item+7,&end, 10);
+ =A0 =A0 =A0 =A0 =A0 =A0if (end = =3D=3D NULL || *end) {
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0derror( &= quot;bad parameter: expecting signal=3D<number>, got '%s'&quo= t;,
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0item );
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0 =A0 =A0= }
+ =A0 =A0 =A0 =A0 =A0 =A0if (signal <=3D 0) {
+ = =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0derror( "bad parameter: signal number m= ust be > 0" );
+ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0 = =A0 =A0}
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0else if ( = !memcmp(item, "reads=3D", 6) ) {
+ =A0 =A0 =A0 =A0 =A0 = =A0reads =3D parse_nand_rw_limit(item+6);
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0else if ( !memcmp(item, "writes=3D", 7) ) {=
+ =A0 =A0 =A0 =A0 =A0 =A0writes =3D parse_nand_rw_limit(item+7);=
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0else {
+= =A0 =A0 =A0 =A0 =A0 =A0derror( "bad parameter '%s' (see -help= -nand-limits)", item );
+ =A0 =A0 =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0 =A0 =A0}
+ =A0 =A0 =A0 =A0item =3D next;
+ =A0 =A0}
+ =A0 =A0= if (pid < 0) {
+ =A0 =A0 =A0 =A0derror( "bad paramater: m= issing pid=3D<number>" );
+ =A0 =A0 =A0 =A0exit(1);
+ =A0 =A0}
+ =A0 =A0else if (= signal < 0) {
+ =A0 =A0 =A0 =A0derror( "bad parameter: mi= ssing signal=3D<number>" );
+ =A0 =A0 =A0 =A0exit(1);<= /div>
+ =A0 =A0}
+ =A0 =A0else if (reads =3D=3D 0 && = writes =3D=3D 0) {
+ =A0 =A0 =A0 =A0dwarning( "no read or write limit specified. ign= oring -nand-limits" );
+ =A0 =A0} else {
+ =A0 =A0= =A0 =A0nand_threshold* =A0t;
+
+ =A0 =A0 =A0 =A0t =A0= =3D &android_nand_read_threshold;
+ =A0 =A0 =A0 =A0t->pid =A0 =A0 =3D pid;
+ =A0 =A0 =A0 = =A0t->signal =A0=3D signal;
+ =A0 =A0 =A0 =A0t->counter =3D= 0;
+ =A0 =A0 =A0 =A0t->limit =A0 =3D reads;
+
=
+ =A0 =A0 =A0 =A0t =A0=3D &android_nand_write_threshold;
+ =A0 =A0 =A0 =A0t->pid =A0 =A0 =3D pid;
+ =A0 =A0 =A0 = =A0t->signal =A0=3D signal;
+ =A0 =A0 =A0 =A0t->counter =3D= 0;
+ =A0 =A0 =A0 =A0t->limit =A0 =3D writes;
+ =A0 = =A0}
+}
+#endif /* CONFIG_NAND_LIMITS */
diff --git a/hw/goldfish_nand.h b/hw/goldfish_nand.h
new fil= e mode 100644
index 0000000..a8f3652
--- /dev/null
+++ b/hw/goldfish_nand.h
@@ -0,0 +1,29 @@
+/* Cop= yright (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 S= oftware Foundation, and
+** may be copied, distributed, and modif= ied under those terms.
+**
+** This program is distributed in the hope that it will= be useful,
+** but WITHOUT ANY WARRANTY; without even the implie= d warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PUR= POSE. =A0See 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(cons= t char *arg);
+void parse_nand_limits(char* =A0limits);
+
+typed= ef struct {
+ =A0 =A0uint64_t =A0 =A0 limit;
+ =A0 =A0u= int64_t =A0 =A0 counter;
+ =A0 =A0int =A0 =A0 =A0 =A0 =A0pid;
+ =A0 =A0int =A0 =A0 =A0 =A0 =A0signal;
+} nand_threshold;
+
+extern nand_threshold =A0 an= droid_nand_read_threshold;
+extern nand_threshold =A0 android_nan= d_write_threshold;
+
+#endif
diff --git a/hw/= goldfish_nand_reg.h b/hw/goldfish_nand_reg.h
new file mode 100644
index 0000000..ea91461
--- /d= ev/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 S= oftware Foundation, and
+** may be copied, distributed, and modif= ied under those terms.
+**
+** This program is distributed in the hope that it will= be useful,
+** but WITHOUT ANY WARRANTY; without even the implie= d warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PUR= POSE. =A0See 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, =A0// Write device name for NAND_DEV to = NAND_DATA (vaddr)
+ NAND_CMD_READ,
+ NAND_CMD_WRITE,
+<= span style=3D"white-space:pre-wrap"> 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 =3D 0x00000001
+};
+
+#define NAND_VERSION_CURRENT (1)
+
+enum nand_reg {
+ // Global
+ <= /span>NAND_VERSION =A0 =A0 =A0 =A0=3D 0x000,
+ NAND_NUM_DEV =A0 =A0 =A0= =A0=3D 0x004,
+ NAND= _DEV =A0 =A0 =A0 =A0 =A0 =A0=3D 0x008,
+
+ // Dev info
+ NAND_DEV_FLAGS =A0 =A0 = =A0=3D 0x010,
+ NAND_= DEV_NAME_LEN =A0 =3D 0x014,
+ NAND_DEV_PAGE_SIZE =A0=3D 0x018,
+ NAND_DEV_EXTRA_SIZE =3D = 0x01c,
+ NAND_DEV_ERA= SE_SIZE =3D 0x020,
+ = NAND_DEV_SIZE_LOW =A0 =3D 0x028,
+ NAND_DEV_SIZE_HIGH =A0= =3D 0x02c,
+
+ // Command
+ NAND= _RESULT =A0 =A0 =A0 =A0 =3D 0x040,
+ NAND_COMMAND =A0 =A0 =A0= =A0=3D 0x044,
+ NAND= _DATA =A0 =A0 =A0 =A0 =A0 =3D 0x048,
+ NAND_TRANSFER_SIZE =A0=3D 0x04c,
+ NAND_ADDR_LOW =A0 =A0 = =A0 =3D 0x050,
+ NAND= _ADDR_HIGH =A0 =A0 =A0=3D 0x054,
+};
+
+#endif
--=A0
1.7.4.1

--00163649982d92628004ab14d90a--