aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--android/hw-pipe-net.c6
-rw-r--r--android/hw-qemud.c203
-rw-r--r--hw/goldfish_pipe.c232
-rw-r--r--hw/goldfish_pipe.h21
-rw-r--r--hw/hw.h5
-rw-r--r--savevm.c40
6 files changed, 464 insertions, 43 deletions
diff --git a/android/hw-pipe-net.c b/android/hw-pipe-net.c
index 193d60b..c2a7e9e 100644
--- a/android/hw-pipe-net.c
+++ b/android/hw-pipe-net.c
@@ -442,6 +442,8 @@ static const GoldfishPipeFuncs netPipeTcp_funcs = {
netPipe_recvBuffers,
netPipe_poll,
netPipe_wakeOn,
+ NULL, /* we can't save these */
+ NULL, /* we can't load these */
};
#ifndef _WIN32
@@ -452,6 +454,8 @@ static const GoldfishPipeFuncs netPipeUnix_funcs = {
netPipe_recvBuffers,
netPipe_poll,
netPipe_wakeOn,
+ NULL, /* we can't save these */
+ NULL, /* we can't load these */
};
#endif
@@ -517,6 +521,8 @@ static const GoldfishPipeFuncs openglesPipe_funcs = {
netPipe_recvBuffers,
netPipe_poll,
netPipe_wakeOn,
+ NULL, /* we can't save these */
+ NULL, /* we can't load these */
};
void
diff --git a/android/hw-qemud.c b/android/hw-qemud.c
index 0820e4c..df9ab21 100644
--- a/android/hw-qemud.c
+++ b/android/hw-qemud.c
@@ -20,6 +20,7 @@
#include "qemu-char.h"
#include "charpipe.h"
#include "cbuffer.h"
+#include "utils/panic.h"
#define D(...) VERBOSE_PRINT(qemud,__VA_ARGS__)
#define D_ACTIVE VERBOSE_CHECK(qemud)
@@ -927,16 +928,16 @@ static QemudClient* qemud_service_connect_client( QemudService *sv,
const char* client_param);
/* Saves the client state needed to re-establish connections on load.
+ * Note that we save only serial clients here. The pipe clients will be
+ * saved along with the pipe to which they are attached.
*/
static void
-qemud_client_save(QEMUFile* f, QemudClient* c)
+qemud_serial_client_save(QEMUFile* f, QemudClient* c)
{
/* save generic information */
qemud_service_save_name(f, c->service);
- qemu_put_be32(f, c->protocol);
- if (!_is_pipe_client(c)) {
- qemu_put_be32(f, c->ProtocolSelector.Serial.channel);
- }
+ qemu_put_string(f, c->param);
+ qemu_put_be32(f, c->ProtocolSelector.Serial.channel);
/* save client-specific state */
if (c->clie_save)
@@ -957,14 +958,16 @@ qemud_client_save(QEMUFile* f, QemudClient* c)
/* Loads client state from file, then starts a new client connected to the
* corresponding service.
+ * Note that we load only serial clients here. The pipe clients will be
+ * loaded along with the pipe to which they were attached.
*/
static int
-qemud_client_load(QEMUFile* f, QemudService* current_services, int version )
+qemud_serial_client_load(QEMUFile* f, QemudService* current_services, int version )
{
char *service_name = qemud_service_load_name(f);
if (service_name == NULL)
return -EIO;
-
+ char* param = qemu_get_string(f);
/* get current service instance */
QemudService *sv = qemud_service_find(current_services, service_name);
if (sv == NULL) {
@@ -973,18 +976,7 @@ qemud_client_load(QEMUFile* f, QemudService* current_services, int version )
return -EIO;
}
- int channel = -1;
-
- if (version >= 2) {
- /* get protocol. */
- QemudProtocol protocol = qemu_get_be32(f);
- /* get channel id */
- if (protocol == QEMUD_PROTOCOL_SERIAL) {
- channel = qemu_get_be32(f);
- }
- } else {
- channel = qemu_get_be32(f);
- }
+ int channel = qemu_get_be32(f);
if (channel == 0) {
D("%s: illegal snapshot: client for control channel must no be saved\n",
@@ -993,7 +985,7 @@ qemud_client_load(QEMUFile* f, QemudService* current_services, int version )
}
/* re-connect client */
- QemudClient* c = qemud_service_connect_client(sv, channel, NULL);
+ QemudClient* c = qemud_service_connect_client(sv, channel, param);
if(c == NULL)
return -EIO;
@@ -1717,8 +1709,9 @@ qemud_client_save_count(QEMUFile* f, QemudClient* c)
{
unsigned int client_count = 0;
for( ; c; c = c->next) // walk over linked list
- // skip control channel, which is not saved
- if (_is_pipe_client(c) || c->ProtocolSelector.Serial.channel > 0)
+ /* skip control channel, which is not saved, and pipe channels that
+ * are saved along with the pipe. */
+ if (!_is_pipe_client(c) && c->ProtocolSelector.Serial.channel > 0)
client_count++;
qemu_put_be32(f, client_count);
@@ -1760,9 +1753,9 @@ qemud_save(QEMUFile* f, void* opaque)
qemud_client_save_count(f, m->clients);
QemudClient *c;
for (c = m->clients; c; c = c->next) {
- /* skip control channel client */
- if (_is_pipe_client(c) || c->ProtocolSelector.Serial.channel > 0) {
- qemud_client_save(f, c);
+ /* skip control channel, and pipe clients */
+ if (!_is_pipe_client(c) && c->ProtocolSelector.Serial.channel > 0) {
+ qemud_serial_client_save(f, c);
}
}
@@ -1806,7 +1799,7 @@ qemud_load_clients(QEMUFile* f, QemudMultiplexer* m, int version )
int client_count = qemu_get_be32(f);
int i, ret;
for (i = 0; i < client_count; i++) {
- if ((ret = qemud_client_load(f, m->services, version))) {
+ if ((ret = qemud_serial_client_load(f, m->services, version))) {
return ret;
}
}
@@ -1839,6 +1832,46 @@ qemud_load(QEMUFile *f, void* opaque, int version)
*
* ----------------------------------------------------------------------------*/
+/* Saves pending pipe message to the snapshot file. */
+static void
+_save_pipe_message(QEMUFile* f, QemudPipeMessage* msg)
+{
+ qemu_put_be32(f, msg->size);
+ qemu_put_be32(f, msg->offset);
+ qemu_put_buffer(f, msg->message, msg->size);
+}
+
+/* Loads pending pipe messages from the snapshot file.
+ * Return:
+ * List of pending pipe messages loaded from snapshot, or NULL if snapshot didn't
+ * contain saved messages.
+ */
+static QemudPipeMessage*
+_load_pipe_message(QEMUFile* f)
+{
+ QemudPipeMessage* ret = NULL;
+ QemudPipeMessage** next = &ret;
+
+ uint32_t size = qemu_get_be32(f);
+ while (size != 0) {
+ QemudPipeMessage* wrk;
+ ANEW0(wrk);
+ *next = wrk;
+ wrk->size = size;
+ wrk->offset = qemu_get_be32(f);
+ wrk->message = malloc(wrk->size);
+ if (wrk->message == NULL) {
+ APANIC("Unable to allocate buffer for pipe's pending message.");
+ }
+ qemu_get_buffer(f, wrk->message, wrk->size);
+ next = &wrk->next;
+ *next = NULL;
+ size = qemu_get_be32(f);
+ }
+
+ return ret;
+}
+
/* This is a callback that gets invoked when guest is connecting to the service.
*
* Here we will create a new client as well as pipe descriptor representing new
@@ -2039,6 +2072,122 @@ _qemudPipe_wakeOn(void* opaque, int flags)
D("%s: -> %X", __FUNCTION__, flags);
}
+static void
+_qemudPipe_save(void* opaque, QEMUFile* f )
+{
+ QemudPipe* qemud_pipe = (QemudPipe*)opaque;
+ QemudClient* c = qemud_pipe->client;
+ QemudPipeMessage* msg = c->ProtocolSelector.Pipe.messages;
+
+ /* save generic information */
+ qemud_service_save_name(f, c->service);
+ qemu_put_string(f, c->param);
+
+ /* Save pending messages. */
+ while (msg != NULL) {
+ _save_pipe_message(f, msg);
+ msg = msg->next;
+ }
+ /* End of pending messages. */
+ qemu_put_be32(f, 0);
+
+ /* save client-specific state */
+ if (c->clie_save)
+ c->clie_save(f, c, c->clie_opaque);
+
+ /* save framing configuration */
+ qemu_put_be32(f, c->framing);
+ if (c->framing) {
+ qemu_put_be32(f, c->need_header);
+ /* header sink always connected to c->header0, no need to save */
+ qemu_put_be32(f, FRAME_HEADER_SIZE);
+ qemu_put_buffer(f, c->header0, FRAME_HEADER_SIZE);
+ /* payload sink */
+ qemud_sink_save(f, c->payload);
+ qemu_put_buffer(f, c->payload->buff, c->payload->size);
+ }
+}
+
+static void*
+_qemudPipe_load(void* hwpipe, void* pipeOpaque, const char* args, QEMUFile* f)
+{
+ QemudPipe* qemud_pipe = NULL;
+ char* param;
+ char *service_name = qemud_service_load_name(f);
+ if (service_name == NULL)
+ return NULL;
+ /* get service instance for the loading client*/
+ QemudService *sv = qemud_service_find(_multiplexer->services, service_name);
+ if (sv == NULL) {
+ D("%s: load failed: unknown service \"%s\"\n",
+ __FUNCTION__, service_name);
+ return NULL;
+ }
+
+ /* Load saved parameters. */
+ param = qemu_get_string(f);
+
+ /* re-connect client */
+ QemudClient* c = qemud_service_connect_client(sv, -1, param);
+ if(c == NULL)
+ return NULL;
+
+ /* Load pending messages. */
+ c->ProtocolSelector.Pipe.messages = _load_pipe_message(f);
+
+ /* load client-specific state */
+ if (c->clie_load && c->clie_load(f, c, c->clie_opaque)) {
+ /* load failure */
+ return NULL;
+ }
+
+ /* load framing configuration */
+ c->framing = qemu_get_be32(f);
+ if (c->framing) {
+
+ /* header buffer */
+ c->need_header = qemu_get_be32(f);
+ int header_size = qemu_get_be32(f);
+ if (header_size > FRAME_HEADER_SIZE) {
+ D("%s: load failed: payload buffer requires %d bytes, %d available\n",
+ __FUNCTION__, header_size, FRAME_HEADER_SIZE);
+ return NULL;
+ }
+ int ret;
+ if ((ret = qemu_get_buffer(f, c->header0, header_size)) != header_size) {
+ D("%s: frame header buffer load failed: expected %d bytes, got %d\n",
+ __FUNCTION__, header_size, ret);
+ return NULL;
+ }
+
+ /* payload sink */
+ if ((ret = qemud_sink_load(f, c->payload)))
+ return NULL;
+
+ /* replace payload buffer by saved data */
+ if (c->payload->buff) {
+ AFREE(c->payload->buff);
+ }
+ AARRAY_NEW(c->payload->buff, c->payload->size+1); /* +1 for terminating zero */
+ if ((ret = qemu_get_buffer(f, c->payload->buff, c->payload->size)) != c->payload->size) {
+ D("%s: frame payload buffer load failed: expected %d bytes, got %d\n",
+ __FUNCTION__, c->payload->size, ret);
+ AFREE(c->payload->buff);
+ return NULL;
+ }
+ }
+
+ /* Associate the client with the pipe. */
+ ANEW0(qemud_pipe);
+ qemud_pipe->hwpipe = hwpipe;
+ qemud_pipe->looper = pipeOpaque;
+ qemud_pipe->service = sv;
+ qemud_pipe->client = c;
+ c->ProtocolSelector.Pipe.qemud_pipe = qemud_pipe;
+
+ return qemud_pipe;
+}
+
/* QEMUD pipe functions.
*/
static const GoldfishPipeFuncs _qemudPipe_funcs = {
@@ -2048,6 +2197,8 @@ static const GoldfishPipeFuncs _qemudPipe_funcs = {
_qemudPipe_recvBuffers,
_qemudPipe_poll,
_qemudPipe_wakeOn,
+ _qemudPipe_save,
+ _qemudPipe_load,
};
/* Initializes QEMUD pipe interface.
diff --git a/hw/goldfish_pipe.c b/hw/goldfish_pipe.c
index b3c6975..3c076f1 100644
--- a/hw/goldfish_pipe.c
+++ b/hw/goldfish_pipe.c
@@ -52,6 +52,11 @@
/* Set to 1 to enable the 'throttle' pipe type, useful for debugging */
#define DEBUG_THROTTLE_PIPE 1
+/* Maximum length of pipe service name, in characters (excluding final 0) */
+#define MAX_PIPE_SERVICE_NAME_SIZE 255
+
+#define GOLDFISH_PIPE_SAVE_VERSION 1
+
/***********************************************************************
***********************************************************************
*****
@@ -85,6 +90,10 @@ goldfish_pipe_add_type(const char* pipeName,
APANIC("Too many goldfish pipe services (%d)", count);
}
+ if (strlen(pipeName) > MAX_PIPE_SERVICE_NAME_SIZE) {
+ APANIC("Pipe service name too long: '%s'", pipeName);
+ }
+
list->services[count].name = pipeName;
list->services[count].opaque = pipeOpaque;
list->services[count].funcs = pipeFuncs[0];
@@ -120,24 +129,33 @@ typedef struct PipeDevice PipeDevice;
typedef struct Pipe {
struct Pipe* next;
struct Pipe* next_waked;
- PipeDevice* device;
- uint32_t channel;
- void* opaque;
- const GoldfishPipeFuncs* funcs;
- unsigned char wanted;
- char closed;
+ PipeDevice* device;
+ uint32_t channel;
+ void* opaque;
+ const GoldfishPipeFuncs* funcs;
+ const PipeService* service;
+ char* args;
+ unsigned char wanted;
+ char closed;
} Pipe;
/* Forward */
static void* pipeConnector_new(Pipe* pipe);
-Pipe*
-pipe_new(uint32_t channel, PipeDevice* dev)
+static Pipe*
+pipe_new0(PipeDevice* dev)
{
Pipe* pipe;
ANEW0(pipe);
+ pipe->device = dev;
+ return pipe;
+}
+
+static Pipe*
+pipe_new(uint32_t channel, PipeDevice* dev)
+{
+ Pipe* pipe = pipe_new0(dev);
pipe->channel = channel;
- pipe->device = dev;
pipe->opaque = pipeConnector_new(pipe);
return pipe;
}
@@ -199,6 +217,97 @@ pipe_list_remove_waked( Pipe** list, Pipe* pipe )
}
}
+static void
+pipe_save( Pipe* pipe, QEMUFile* file )
+{
+ if (pipe->service == NULL) {
+ /* pipe->service == NULL means we're still using a PipeConnector */
+ /* Write a zero to indicate this condition */
+ qemu_put_byte(file, 0);
+ } else {
+ /* Otherwise, write a '1' then the service name */
+ qemu_put_byte(file, 1);
+ qemu_put_string(file, pipe->service->name);
+ }
+
+ /* Now save other common data */
+ qemu_put_be32(file, (unsigned int)pipe->channel);
+ qemu_put_byte(file, (int)pipe->wanted);
+ qemu_put_byte(file, (int)pipe->closed);
+
+ /* Write 1 + args, if any, or simply 0 otherwise */
+ if (pipe->args != NULL) {
+ qemu_put_byte(file, 1);
+ qemu_put_string(file, pipe->args);
+ } else {
+ qemu_put_byte(file, 0);
+ }
+
+ if (pipe->funcs->save) {
+ pipe->funcs->save(pipe->opaque, file);
+ }
+}
+
+static Pipe*
+pipe_load( PipeDevice* dev, QEMUFile* file )
+{
+ Pipe* pipe;
+ const PipeService* service = NULL;
+ int state = qemu_get_byte(file);
+ uint32_t channel;
+
+ if (state != 0) {
+ /* Pipe is associated with a service. */
+ char* name = qemu_get_string(file);
+ if (name == NULL)
+ return NULL;
+
+ service = goldfish_pipe_find_type(name);
+ if (service == NULL) {
+ D("No QEMU pipe service named '%s'", name);
+ AFREE(name);
+ return NULL;
+ }
+ }
+
+ channel = qemu_get_be32(file);
+ pipe = pipe_new(channel, dev);
+ pipe->wanted = qemu_get_byte(file);
+ pipe->closed = qemu_get_byte(file);
+ if (qemu_get_byte(file) != 0) {
+ pipe->args = qemu_get_string(file);
+ }
+
+ pipe->service = service;
+ if (service != NULL) {
+ pipe->funcs = &service->funcs;
+ }
+
+ if (pipe->funcs->load) {
+ pipe->opaque = pipe->funcs->load(pipe, service->opaque, pipe->args, file);
+ if (pipe->opaque == NULL) {
+ AFREE(pipe);
+ return NULL;
+ }
+ } else {
+ /* Force-close the pipe on load */
+ pipe->closed = 1;
+ }
+ return pipe;
+}
+
+static void
+pipe_free( Pipe* pipe )
+{
+ /* Call close callback */
+ if (pipe->funcs->close) {
+ pipe->funcs->close(pipe->opaque);
+ }
+ /* Free stuff */
+ AFREE(pipe->args);
+ AFREE(pipe);
+}
+
/***********************************************************************
***********************************************************************
*****
@@ -293,6 +402,8 @@ pipeConnector_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int
if (pipeArgs != NULL) {
*pipeArgs++ = '\0';
+ if (!*pipeArgs)
+ pipeArgs = NULL;
}
Pipe* pipe = pcon->pipe;
@@ -310,7 +421,9 @@ pipeConnector_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int
/* Do the evil switch now */
pipe->opaque = peer;
+ pipe->service = svc;
pipe->funcs = &svc->funcs;
+ pipe->args = ASTRDUP(pipeArgs);
AFREE(pcon);
}
@@ -335,6 +448,32 @@ pipeConnector_wakeOn( void* opaque, int flags )
/* nothing, really should never happen */
}
+static void
+pipeConnector_save( void* pipe, QEMUFile* file )
+{
+ PipeConnector* pcon = pipe;
+ qemu_put_sbe32(file, pcon->buffpos);
+ qemu_put_sbuffer(file, (const int8_t*)pcon->buffer, pcon->buffpos);
+}
+
+static void*
+pipeConnector_load( void* hwpipe, void* pipeOpaque, const char* args, QEMUFile* file )
+{
+ PipeConnector* pcon;
+
+ int len = qemu_get_sbe32(file);
+ if (len < 0 || len > sizeof(pcon->buffer)) {
+ return NULL;
+ }
+ pcon = pipeConnector_new(hwpipe);
+ pcon->buffpos = len;
+ if (qemu_get_buffer(file, (uint8_t*)pcon->buffer, pcon->buffpos) != pcon->buffpos) {
+ AFREE(pcon);
+ return NULL;
+ }
+ return pcon;
+}
+
static const GoldfishPipeFuncs pipeConnector_funcs = {
NULL, /* init */
pipeConnector_close, /* should rarely happen */
@@ -342,6 +481,8 @@ static const GoldfishPipeFuncs pipeConnector_funcs = {
pipeConnector_recvBuffers, /* should not happen */
pipeConnector_poll, /* should not happen */
pipeConnector_wakeOn, /* should not happen */
+ pipeConnector_save,
+ pipeConnector_load,
};
/***********************************************************************
@@ -860,12 +1001,7 @@ pipeDevice_doCommand( PipeDevice* dev, uint32_t command )
*lookup = pipe->next;
pipe->next = NULL;
pipe_list_remove_waked(&dev->signaled_pipes, pipe);
- /* Call close callback */
- if (pipe->funcs->close) {
- pipe->funcs->close(pipe->opaque);
- }
- /* Free stuff */
- AFREE(pipe);
+ pipe_free(pipe);
break;
case PIPE_CMD_POLL:
@@ -1018,6 +1154,69 @@ static CPUWriteMemoryFunc *pipe_dev_writefn[] = {
pipe_dev_write
};
+static void
+goldfish_pipe_save( QEMUFile* file, void* opaque )
+{
+ PipeDevice* dev = opaque;
+ Pipe* pipe;
+
+ qemu_put_be32(file, dev->address);
+ qemu_put_be32(file, dev->size);
+ qemu_put_be32(file, dev->status);
+ qemu_put_be32(file, dev->channel);
+ qemu_put_be32(file, dev->wakes);
+
+ /* Count the number of pipe connections */
+ int count = 0;
+ for ( pipe = dev->pipes; pipe; pipe = pipe->next )
+ count++;
+
+ qemu_put_sbe32(file, count);
+
+ /* Now save each pipe one after the other */
+ for ( pipe = dev->pipes; pipe; pipe = pipe->next ) {
+ pipe_save(pipe, file);
+ }
+}
+
+static int
+goldfish_pipe_load( QEMUFile* file, void* opaque, int version_id )
+{
+ PipeDevice* dev = opaque;
+ Pipe* pipe;
+
+ if (version_id != GOLDFISH_PIPE_SAVE_VERSION)
+ return -EINVAL;
+
+ dev->address = qemu_get_be32(file);
+ dev->size = qemu_get_be32(file);
+ dev->status = qemu_get_be32(file);
+ dev->channel = qemu_get_be32(file);
+ dev->wakes = qemu_get_be32(file);
+
+ /* Count the number of pipe connections */
+ int count = qemu_get_sbe32(file);
+
+ /* Load all pipe connections */
+ for ( ; count > 0; count-- ) {
+ pipe = pipe_load(dev, file);
+ if (pipe == NULL) {
+ return -EIO;
+ }
+ pipe->next = dev->pipes;
+ dev->pipes = pipe;
+ }
+
+ /* Now we need to wake/close all relevant pipes */
+ for ( pipe = dev->pipes; pipe; pipe = pipe->next ) {
+ if (pipe->wanted != 0)
+ goldfish_pipe_wake(pipe, pipe->wanted);
+ if (pipe->closed != 0)
+ goldfish_pipe_close(pipe);
+ }
+ return 0;
+}
+
/* initialize the trace device */
void pipe_dev_init()
{
@@ -1034,6 +1233,9 @@ void pipe_dev_init()
goldfish_device_add(&s->dev, pipe_dev_readfn, pipe_dev_writefn, s);
+ register_savevm( "goldfish_pipe", 0, GOLDFISH_PIPE_SAVE_VERSION,
+ goldfish_pipe_save, goldfish_pipe_load, s);
+
#if DEBUG_ZERO_PIPE
goldfish_pipe_add_type("zero", NULL, &zeroPipe_funcs);
#endif
diff --git a/hw/goldfish_pipe.h b/hw/goldfish_pipe.h
index 8074619..f08cef8 100644
--- a/hw/goldfish_pipe.h
+++ b/hw/goldfish_pipe.h
@@ -94,6 +94,27 @@ typedef struct {
* then the pipe implementation shall call goldfish_pipe_wake().
*/
void (*wakeOn)( void* opaque, int flags );
+
+ /* Called to save the pipe's state to a QEMUFile, i.e. when saving
+ * snapshots. This can be NULL to indicate that no state can be saved.
+ * In this case, when the pipe is loaded, the emulator will automatically
+ * force-close so the next operation the guest performs on it will return
+ * a PIPE_ERROR_IO error code.
+ */
+ void (*save)( void* pipe, QEMUFile* file );
+
+ /* Called to load the sate of a pipe from a QEMUFile. This will always
+ * correspond to the state of the pipe as saved by a previous call to
+ * the 'save' method. Can be NULL to indicate that the pipe state cannot
+ * be loaded. In this case, the emulator will automatically force-close
+ * it.
+ *
+ * In case of success, this returns 0, and the new pipe object is returned
+ * in '*ppipe'. In case of errno code is returned to indicate a failure.
+ * 'hwpipe' and 'pipeOpaque' are the same arguments than those passed
+ * to 'init'.
+ */
+ void* (*load)( void* hwpipe, void* pipeOpaque, const char* args, QEMUFile* file);
} GoldfishPipeFuncs;
/* Register a new pipe handler type. 'pipeOpaque' is passed directly
diff --git a/hw/hw.h b/hw/hw.h
index d230448..292d42e 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -218,6 +218,11 @@ static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv)
qemu_get_be64s(f, (uint64_t *)pv);
}
+#ifdef CONFIG_ANDROID
+void qemu_put_string(QEMUFile *f, const char* str);
+char* qemu_get_string(QEMUFile *f);
+#endif
+
#ifdef NEED_CPU_H
#if TARGET_LONG_BITS == 64
#define qemu_put_betl qemu_put_be64
diff --git a/savevm.c b/savevm.c
index c08e8fa..7a5dc23 100644
--- a/savevm.c
+++ b/savevm.c
@@ -567,6 +567,37 @@ int qemu_get_byte(QEMUFile *f)
return f->buf[f->buf_index++];
}
+#ifdef CONFIG_ANDROID
+void qemu_put_string(QEMUFile *f, const char* str)
+{
+ /* We will encode NULL and the empty string in the same way */
+ int slen;
+ if (str == NULL) {
+ str = "";
+ }
+ slen = strlen(str);
+ qemu_put_be32(f, slen);
+ qemu_put_buffer(f, (const uint8_t*)str, slen);
+}
+
+char* qemu_get_string(QEMUFile *f)
+{
+ int slen = qemu_get_be32(f);
+ char* str;
+ if (slen == 0)
+ return NULL;
+
+ str = qemu_malloc(slen+1);
+ if (qemu_get_buffer(f, (uint8_t*)str, slen) != slen) {
+ qemu_free(str);
+ return NULL;
+ }
+ str[slen] = '\0';
+ return str;
+}
+#endif
+
+
int64_t qemu_ftell(QEMUFile *f)
{
return f->buf_offset - f->buf_size + f->buf_index;
@@ -858,7 +889,7 @@ void unregister_savevm(const char *idstr, void *opaque)
#define QEMU_VM_FILE_MAGIC 0x5145564d
#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002
-#define QEMU_VM_FILE_VERSION 0x00000003
+#define QEMU_VM_FILE_VERSION 0x00000004
#define QEMU_VM_EOF 0x00
#define QEMU_VM_SECTION_START 0x01
@@ -1075,8 +1106,13 @@ int qemu_loadvm_state(QEMUFile *f)
v = qemu_get_be32(f);
if (v == QEMU_VM_FILE_VERSION_COMPAT)
return qemu_loadvm_state_v2(f);
- if (v != QEMU_VM_FILE_VERSION)
+ if (v < QEMU_VM_FILE_VERSION) {
+ fprintf(stderr, "Snapshot format %d is too old for this version of the emulator, please create a new one.\n", v);
+ return -ENOTSUP;
+ } else if (v > QEMU_VM_FILE_VERSION) {
+ fprintf(stderr, "Snapshot format %d is more recent than the emulator, please update your Android SDK Tools.\n", v);
return -ENOTSUP;
+ }
while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
uint32_t instance_id, version_id, section_id;