aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@google.com>2009-05-25 18:01:03 +0200
committerDavid 'Digit' Turner <digit@google.com>2009-05-26 17:23:25 +0200
commit318e4f294c181df33cf2541763904565b29bcccb (patch)
tree35b5085c9e35e91967e2d4a4db80e28ec086e1d3
parent0d47fe5756b1f243e8d65968cd73c0119363f909 (diff)
downloadexternal_qemu-318e4f294c181df33cf2541763904565b29bcccb.zip
external_qemu-318e4f294c181df33cf2541763904565b29bcccb.tar.gz
external_qemu-318e4f294c181df33cf2541763904565b29bcccb.tar.bz2
This adds the '-prop <name>=<value>' option which is used to set
boot-time system properties from the command line. This is done by implementing a new 'boot-properties' qemud service in the emulator. This is to be used by the 'qemu-props' helper program that will be invoked by /system/etc/init.goldfish.rc to read a list of system properties from the emulator and set them in the emulated system during boot.
-rw-r--r--Makefile.android1
-rw-r--r--android/boot-properties.c214
-rw-r--r--android/boot-properties.h47
-rw-r--r--android/cmdline-option.c61
-rw-r--r--android/cmdline-option.h8
-rw-r--r--android/cmdline-options.h6
-rw-r--r--android/help.c20
-rw-r--r--android/hw-control.c7
-rw-r--r--android/hw-qemud.c16
-rw-r--r--android/hw-qemud.h32
-rw-r--r--android/hw-sensors.c3
-rw-r--r--android/main.c15
-rw-r--r--docs/ANDROID-QEMUD-SERVICES.TXT25
13 files changed, 435 insertions, 20 deletions
diff --git a/Makefile.android b/Makefile.android
index 4c697fb..549136a 100644
--- a/Makefile.android
+++ b/Makefile.android
@@ -421,6 +421,7 @@ VL_SOURCES := vl.c osdep.c cutils.c \
shaper.c charpipe.c loadpng.c \
framebuffer.c \
tcpdump.c \
+ android/boot-properties.c \
android/charmap.c \
android/cmdline-option.c \
android/config.c \
diff --git a/android/boot-properties.c b/android/boot-properties.c
new file mode 100644
index 0000000..1c714e9
--- /dev/null
+++ b/android/boot-properties.c
@@ -0,0 +1,214 @@
+/* Copyright (C) 2009 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 "android/boot-properties.h"
+#include "android/utils/debug.h"
+#include "android/utils/system.h"
+#include "android/hw-qemud.h"
+#include "android/globals.h"
+
+#define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
+
+/* define T_ACTIVE to 1 to debug transport communications */
+#define T_ACTIVE 0
+
+#if T_ACTIVE
+#define T(...) VERBOSE_PRINT(init,__VA_ARGS__)
+#else
+#define T(...) ((void)0)
+#endif
+
+typedef struct BootProperty {
+ struct BootProperty* next;
+ char* property;
+ int length;
+} BootProperty;
+
+static BootProperty*
+boot_property_alloc( const char* name, int namelen,
+ const char* value, int valuelen )
+{
+ int length = namelen + 1 + valuelen;
+ BootProperty* prop = android_alloc( sizeof(*prop) + length + 1 );
+ char* p;
+
+ prop->next = NULL;
+ prop->property = p = (char*)(prop + 1);
+ prop->length = length;
+
+ memcpy( p, name, namelen );
+ p += namelen;
+ *p++ = '=';
+ memcpy( p, value, valuelen );
+ p += valuelen;
+ *p = '\0';
+
+ return prop;
+}
+
+static BootProperty* _boot_properties;
+static BootProperty** _boot_properties_tail = &_boot_properties;
+static int _inited;
+
+/* this code supports the list of system properties that will
+ * be set on boot in the emulated system.
+ */
+
+int
+boot_property_add2( const char* name, int namelen,
+ const char* value, int valuelen )
+{
+ BootProperty* prop;
+
+ /* check the lengths
+ */
+ if (namelen > PROPERTY_MAX_NAME)
+ return -1;
+
+ if (valuelen > PROPERTY_MAX_VALUE)
+ return -2;
+
+ /* check that there are not invalid characters in the
+ * property name
+ */
+ const char* reject = " =$*?'\"";
+ int nn;
+
+ for (nn = 0; nn < namelen; nn++) {
+ if (strchr(reject, name[nn]) != NULL)
+ return -3;
+ }
+
+ /* init service if needed */
+ if (!_inited) {
+ boot_property_init_service();
+ _inited = 1;
+ }
+
+ D("Adding boot property: '%.*s' = '%.*s'",
+ namelen, name, valuelen, value);
+
+ /* add to the internal list */
+ prop = boot_property_alloc(name, namelen, value, valuelen);
+
+ *_boot_properties_tail = prop;
+ _boot_properties_tail = &prop->next;
+
+ return 0;
+}
+
+
+int
+boot_property_add( const char* name, const char* value )
+{
+ int namelen = strlen(name);
+ int valuelen = strlen(value);
+
+ return boot_property_add2(name, namelen, value, valuelen);
+}
+
+
+
+#define SERVICE_NAME "boot-properties"
+
+static void
+boot_property_client_recv( void* opaque,
+ uint8_t* msg,
+ int msglen,
+ QemudClient* client )
+{
+ /* the 'list' command shall send all boot properties
+ * to the client, then close the connection.
+ */
+ if (msglen == 4 && !memcmp(msg, "list", 4)) {
+ BootProperty* prop;
+ for (prop = _boot_properties; prop != NULL; prop = prop->next) {
+ qemud_client_send(client, (uint8_t*)prop->property, prop->length);
+ }
+ qemud_client_close(client);
+ return;
+ }
+
+ /* unknown command ? */
+ D("%s: ignoring unknown command: %.*s", __FUNCTION__, msglen, msg);
+}
+
+static QemudClient*
+boot_property_service_connect( void* opaque,
+ QemudService* serv,
+ int channel )
+{
+ QemudClient* client;
+
+ client = qemud_client_new( serv, channel, NULL,
+ boot_property_client_recv,
+ NULL );
+
+ qemud_client_set_framing(client, 1);
+ return client;
+}
+
+
+void
+boot_property_init_service( void )
+{
+ if (!_inited) {
+ QemudService* serv = qemud_service_register( SERVICE_NAME,
+ 1, NULL,
+ boot_property_service_connect );
+ if (serv == NULL) {
+ derror("could not register '%s' service", SERVICE_NAME);
+ return;
+ }
+ D("registered '%s' qemud service", SERVICE_NAME);
+ }
+}
+
+
+
+void
+boot_property_parse_option( const char* param )
+{
+ char* q = strchr(param,'=');
+ const char* name;
+ const char* value;
+ int namelen, valuelen, ret;
+
+ if (q == NULL) {
+ dwarning("boot property missing (=) separator: %s", param);
+ return;
+ }
+
+ name = param;
+ namelen = q - param;
+
+ value = q+1;
+ valuelen = strlen(name) - (namelen+1);
+
+ ret = boot_property_add2(name, namelen, value, valuelen);
+ if (ret < 0) {
+ switch (ret) {
+ case -1:
+ dwarning("boot property name too long: '%.*s'",
+ namelen, name);
+ break;
+ case -2:
+ dwarning("boot property value too long: '%.*s'",
+ valuelen, value);
+ break;
+ case -3:
+ dwarning("boot property name contains invalid chars: %.*s",
+ namelen, name);
+ break;
+ }
+ }
+}
diff --git a/android/boot-properties.h b/android/boot-properties.h
new file mode 100644
index 0000000..6711c59
--- /dev/null
+++ b/android/boot-properties.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2009 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 _ANDROID_BOOT_PROPERTIES_H
+#define _ANDROID_BOOT_PROPERTIES_H
+
+/* these values give the maximum length of system property
+ * names and values. They must match the corresponding definitions
+ * in the Android source tree (in system/core/include/cutils/properties.h)
+ */
+#define PROPERTY_MAX_NAME 32
+#define PROPERTY_MAX_VALUE 92
+
+/* record a new boot system property, this must be performed before the
+ * VM is started. Returns 0 on success, or < 0 on error.
+ * Possible errors are:
+ * -1 property name too long
+ * -2 property value too long
+ * -3 invalid characters in property name
+ */
+int boot_property_add( const char* name, const char* value );
+
+/* same as boot_property_add, but allows to use non-zero terminated strings.
+ */
+int boot_property_add2( const char* name, int namelen,
+ const char* value, int valuelen );
+
+/* init the boot property QEMUD service. This must be performed before
+ * the VM is started. This is also performed automatically if you call
+ * boot_property_add().
+ */
+void boot_property_init_service( void );
+
+/* parse the parameter the list of -prop options passed on the command line
+ */
+void boot_property_parse_option( const char* param );
+
+#endif /* _ANDROID_BOOT_PROPERTIES_H */
diff --git a/android/cmdline-option.c b/android/cmdline-option.c
index b773d9d..fb5aa23 100644
--- a/android/cmdline-option.c
+++ b/android/cmdline-option.c
@@ -1,6 +1,7 @@
#include "android/cmdline-option.h"
#include "android/utils/debug.h"
#include "android/utils/misc.h"
+#include "android/utils/system.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
@@ -15,11 +16,16 @@ debug_tags[] = {
static void parse_debug_tags( const char* tags );
void parse_env_debug_tags( void );
+enum {
+ OPTION_IS_FLAG = 0,
+ OPTION_IS_PARAM,
+ OPTION_IS_LIST,
+};
typedef struct {
const char* name;
int var_offset;
- int var_is_param;
+ int var_type;
int var_is_config;
} OptionInfo;
@@ -28,10 +34,11 @@ typedef struct {
static const OptionInfo option_keys[] = {
-#define OPT_PARAM(_name,_template,_descr) OPTION(_name,1,0)
-#define OPT_FLAG(_name,_descr) OPTION(_name,0,0)
-#define CFG_PARAM(_name,_template,_descr) OPTION(_name,1,1)
-#define CFG_FLAG(_name,_descr) OPTION(_name,0,1)
+#define OPT_FLAG(_name,_descr) OPTION(_name,OPTION_IS_FLAG,0)
+#define OPT_PARAM(_name,_template,_descr) OPTION(_name,OPTION_IS_PARAM,0)
+#define OPT_LIST(_name,_template,_descr) OPTION(_name,OPTION_IS_LIST,0)
+#define CFG_FLAG(_name,_descr) OPTION(_name,OPTION_IS_FLAG,1)
+#define CFG_PARAM(_name,_template,_descr) OPTION(_name,OPTION_IS_PARAM,1)
#include "android/cmdline-options.h"
{ NULL, 0, 0, 0 }
};
@@ -145,15 +152,31 @@ android_parse_options( int *pargc, char** *pargv, AndroidOptions* opt )
if ( !strcmp( oo->name, arg2 ) ) {
void* field = (char*)opt + oo->var_offset;
- if (oo->var_is_param) {
- /* parameter option */
+ if (oo->var_type != OPTION_IS_FLAG) {
+ /* parameter/list option */
if (nargs == 0) {
derror( "-%s must be followed by parameter (see -help-%s)",
arg, arg );
exit(1);
}
nargs--;
- ((char**)field)[0] = *aread++;
+
+ if (oo->var_type == OPTION_IS_PARAM)
+ {
+ ((char**)field)[0] = *aread++;
+ }
+ else if (oo->var_type == OPTION_IS_LIST)
+ {
+ ParamList** head = (ParamList**)field;
+ ParamList* pl;
+ ANEW0(pl);
+ /* note: store list items in reverse order here
+ * the list is reversed later in this function.
+ */
+ pl->param = *aread++;
+ pl->next = *head;
+ *head = pl;
+ }
} else {
/* flag option */
((int*)field)[0] = 1;
@@ -182,6 +205,28 @@ android_parse_options( int *pargc, char** *pargv, AndroidOptions* opt )
awrite[0] = NULL;
+ /* reverse any parameter list before exit.
+ */
+ {
+ const OptionInfo* oo = option_keys;
+
+ for ( ; oo->name; oo++ ) {
+ if ( oo->var_type == OPTION_IS_LIST ) {
+ ParamList** head = (ParamList**)((char*)opt + oo->var_offset);
+ ParamList* prev = NULL;
+ ParamList* cur = *head;
+
+ while (cur != NULL) {
+ ParamList* next = cur->next;
+ cur->next = prev;
+ prev = cur;
+ cur = next;
+ }
+ *head = prev;
+ }
+ }
+ }
+
return 0;
}
diff --git a/android/cmdline-option.h b/android/cmdline-option.h
index b87144d..90a7e64 100644
--- a/android/cmdline-option.h
+++ b/android/cmdline-option.h
@@ -12,9 +12,17 @@
#ifndef _ANDROID_OPTION_H
#define _ANDROID_OPTION_H
+/* a structure used to model a linked list of parameters
+ */
+typedef struct ParamList {
+ char* param;
+ struct ParamList* next;
+} ParamList;
+
/* define a structure that will hold all option variables
*/
typedef struct {
+#define OPT_LIST(n,t,d) ParamList* n;
#define OPT_PARAM(n,t,d) char* n;
#define OPT_FLAG(n,d) int n;
#include "android/cmdline-options.h"
diff --git a/android/cmdline-options.h b/android/cmdline-options.h
index 5291177..0689e83 100644
--- a/android/cmdline-options.h
+++ b/android/cmdline-options.h
@@ -5,6 +5,9 @@
#ifndef OPT_PARAM
#error OPT_PARAM is not defined
#endif
+#ifndef OPT_LIST
+#error OPT_LIST is not defined
+#endif
#ifndef OPT_FLAG
#error OPT_FLAG is not defined
#endif
@@ -128,6 +131,8 @@ OPT_PARAM( tcpdump, "<file>", "capture network packets to file" )
OPT_PARAM( bootchart, "<timeout>", "enable bootcharting")
+OPT_LIST( prop, "<name>=<value>", "set system property on boot")
+
#ifdef CONFIG_NAND_LIMITS
OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds" )
#endif
@@ -137,3 +142,4 @@ OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds"
#undef CFG_PARAM
#undef OPT_FLAG
#undef OPT_PARAM
+#undef OPT_LIST
diff --git a/android/help.c b/android/help.c
index 391b63d..d01ccc9 100644
--- a/android/help.c
+++ b/android/help.c
@@ -5,6 +5,7 @@
#include "android/utils/debug.h"
#include "android/utils/misc.h"
#include "android/skin/keyset.h"
+#include "android/boot-properties.h"
#include "android/android.h"
#include <stdint.h>
#include "audio/audio.h"
@@ -1281,6 +1282,24 @@ help_tcpdump(stralloc_t *out)
);
}
+static void
+help_prop(stralloc_t *out)
+{
+ PRINTF(
+ " use '-prop <name>=<value>' to set a boot-time system property.\n"
+ " <name> must be a property name of at most %d characters, without any\n"
+ " space in it, and <value> must be a string of at most %d characters.\n\n",
+ PROPERTY_MAX_NAME, PROPERTY_MAX_VALUE );
+
+ PRINTF(
+ " the corresponding system property will be set at boot time in the\n"
+ " emulated system. This can be useful for debugging purposes.\n\n"
+
+ " note that you can use several -prop options to define more than one\n"
+ " boot property.\n\n"
+ );
+}
+
#define help_no_skin NULL
#define help_netspeed help_shaper
#define help_netdelay help_shaper
@@ -1307,6 +1326,7 @@ typedef struct {
static const OptionHelp option_help[] = {
#define OPT_FLAG(_name,_descr) { STRINGIFY(_name), NULL, _descr, help_##_name },
#define OPT_PARAM(_name,_template,_descr) { STRINGIFY(_name), _template, _descr, help_##_name },
+#define OPT_LIST OPT_PARAM
#include "android/cmdline-options.h"
{ NULL, NULL, NULL, NULL }
};
diff --git a/android/hw-control.c b/android/hw-control.c
index a82a92a..d99f7e3 100644
--- a/android/hw-control.c
+++ b/android/hw-control.c
@@ -51,9 +51,10 @@ static void hw_control_do_query( HwControl* h, uint8_t* query, int querylen
/* called when a qemud client sends a command */
static void
-_hw_control_qemud_client_recv( void* opaque,
- uint8_t* msg,
- int msglen )
+_hw_control_qemud_client_recv( void* opaque,
+ uint8_t* msg,
+ int msglen,
+ QemudClient* client )
{
hw_control_do_query(opaque, msg, msglen);
}
diff --git a/android/hw-qemud.c b/android/hw-qemud.c
index ba4ab42..efe6a99 100644
--- a/android/hw-qemud.c
+++ b/android/hw-qemud.c
@@ -545,7 +545,7 @@ qemud_client_recv( void* opaque, uint8_t* msg, int msglen )
/* no framing, things are simple */
if (!c->framing) {
if (c->clie_recv)
- c->clie_recv( c->clie_opaque, msg, msglen );
+ c->clie_recv( c->clie_opaque, msg, msglen, c );
return;
}
@@ -566,7 +566,7 @@ qemud_client_recv( void* opaque, uint8_t* msg, int msglen )
if (c->clie_recv)
c->clie_recv( c->clie_opaque,
msg+FRAME_HEADER_SIZE,
- msglen-FRAME_HEADER_SIZE);
+ msglen-FRAME_HEADER_SIZE, c );
return;
}
}
@@ -606,7 +606,7 @@ qemud_client_recv( void* opaque, uint8_t* msg, int msglen )
if (c->clie_recv)
- c->clie_recv( c->clie_opaque, c->payload->buff, c->payload->size );
+ c->clie_recv( c->clie_opaque, c->payload->buff, c->payload->size, c );
AFREE(c->payload->buff);
c->need_header = 1;
@@ -893,9 +893,10 @@ qemud_multiplexer_disconnect( QemudMultiplexer* m,
* (i.e. msg[msglen] is a valid memory read that returns '\0')
*/
static void
-qemud_multiplexer_control_recv( void* opaque,
- uint8_t* msg,
- int msglen )
+qemud_multiplexer_control_recv( void* opaque,
+ uint8_t* msg,
+ int msglen,
+ QemudClient* client )
{
QemudMultiplexer* mult = opaque;
uint8_t* msgend = msg + msglen;
@@ -1226,7 +1227,8 @@ typedef struct {
* this simply sends the message through the charpipe to the user.
*/
static void
-_qemud_char_client_recv( void* opaque, uint8_t* msg, int msglen )
+_qemud_char_client_recv( void* opaque, uint8_t* msg, int msglen,
+ QemudClient* client )
{
CharDriverState* cs = opaque;
qemu_chr_write(cs, msg, msglen);
diff --git a/android/hw-qemud.h b/android/hw-qemud.h
index 5b45a94..8aae74d 100644
--- a/android/hw-qemud.h
+++ b/android/hw-qemud.h
@@ -62,28 +62,58 @@ typedef struct QemudClient QemudClient;
typedef struct QemudService QemudService;
+/* A function that will be called when the client running in the emulated
+ * system has closed its connection to qemud.
+ */
typedef void (*QemudClientClose)( void* opaque );
-typedef void (*QemudClientRecv) ( void* opaque, uint8_t* msg, int msglen );
+/* A function that will be called when the client sends a message to the
+ * service through qemud.
+ */
+typedef void (*QemudClientRecv) ( void* opaque, uint8_t* msg, int msglen, QemudClient* client );
+
+/* Register a new client for a given service.
+ * 'clie_opaque' will be sent as the first argument to 'clie_recv' and 'clie_close'
+ * 'clie_recv' and 'clie_close' are both optional and may be NULL.
+ *
+ * You should typically use this function within a QemudServiceConnect callback
+ * (see below).
+ */
extern QemudClient* qemud_client_new( QemudService* service,
int channel_id,
void* clie_opaque,
QemudClientRecv clie_recv,
QemudClientClose clie_close );
+/* Enable framing on a given client channel.
+ */
extern void qemud_client_set_framing( QemudClient* client, int enabled );
+/* Send a message to a given qemud client
+ */
extern void qemud_client_send ( QemudClient* client, const uint8_t* msg, int msglen );
+
+/* Force-close the connection to a given qemud client.
+ */
extern void qemud_client_close( QemudClient* client );
+/* A function that will be called each time a new client in the emulated
+ * system tries to connect to a given qemud service. This should typically
+ * call qemud_client_new() to register a new client.
+ */
typedef QemudClient* (*QemudServiceConnect)( void* opaque, QemudService* service, int channel );
+/* Register a new qemud service.
+ * 'serv_opaque' is the first parameter to 'serv_connect'
+ */
extern QemudService* qemud_service_register( const char* serviceName,
int max_clients,
void* serv_opaque,
QemudServiceConnect serv_connect );
+/* Sends a message to all clients of a given service.
+ */
extern void qemud_service_broadcast( QemudService* sv,
const uint8_t* msg,
int msglen );
diff --git a/android/hw-sensors.c b/android/hw-sensors.c
index eb2cc78..37d4af7 100644
--- a/android/hw-sensors.c
+++ b/android/hw-sensors.c
@@ -158,7 +158,8 @@ static void hw_sensors_timer_tick(void* opaque);
/* Qemud service management */
static void
-_hw_sensors_qemud_client_recv( void* opaque, uint8_t* msg, int msglen )
+_hw_sensors_qemud_client_recv( void* opaque, uint8_t* msg, int msglen,
+ QemudClient* client )
{
hw_sensors_receive(opaque, msg, msglen);
}
diff --git a/android/main.c b/android/main.c
index ed005d2..cd4162a 100644
--- a/android/main.c
+++ b/android/main.c
@@ -52,6 +52,7 @@
#include "android/hw-kmsg.h"
#include "android/hw-control.h"
#include "android/hw-sensors.h"
+#include "android/boot-properties.h"
#include "android/user-config.h"
#include "android/utils/bufprint.h"
#include "android/utils/dirscanner.h"
@@ -2493,6 +2494,20 @@ int main(int argc, char **argv)
}
}
+ /* start the 'boot-properties service, and parse the -prop
+ * options, if any.
+ */
+ boot_property_init_service();
+
+ if (opts->prop != NULL) {
+ ParamList* pl = opts->prop;
+ for ( ; pl != NULL; pl = pl->next ) {
+ boot_property_parse_option(pl->param);
+ }
+ }
+
+ /* Setup the kernel init options
+ */
{
static char params[1024];
char *p = params, *end = p + sizeof(params);
diff --git a/docs/ANDROID-QEMUD-SERVICES.TXT b/docs/ANDROID-QEMUD-SERVICES.TXT
index 24636e5..4ac53f4 100644
--- a/docs/ANDROID-QEMUD-SERVICES.TXT
+++ b/docs/ANDROID-QEMUD-SERVICES.TXT
@@ -153,3 +153,28 @@ image.
Implementation: android/hw-sensors.c
Since: SDK 1.5 (cupcake)
+
+
+"boot-properties" service:
+--------------------------
+
+ This service is used to set system properties in the emulated system at
+ boot time. It is invoked by the 'qemu-props' helper program that is invoked
+ by /system/etc/init.goldfish.rc. All messages are framed and the protocol
+ is the following:
+
+ 1/ Clients sends the 'list' command
+
+ 2/ Service answers by listing all system properties to set. One per
+ message with the following format:
+
+ <property-name>=<property-value>
+
+ Note that <property-value> is not zero-terminated.
+
+
+ 3/ After sending all the properties, the service force-closes the
+ connection.
+
+ Implementation: android/boot-properties.c
+ Since: SDK 1.XXX (donut)