From 3c06277bbb6ea135e6a1daf22463a347fc7898c7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 26 May 2009 11:05:58 +0100 Subject: [PATCH] [settings] Allow for arbitrarily-named settings This provides a mechanism for using arbitrarily-named variables within gPXE, using the existing syntax for settings. --- src/core/settings.c | 296 ++++++++++++++++++++----- src/include/gpxe/netdevice.h | 19 +- src/include/gpxe/settings.h | 41 ++-- src/interface/smbios/smbios_settings.c | 17 -- src/net/netdev_settings.c | 18 +- src/net/netdevice.c | 5 +- 6 files changed, 297 insertions(+), 99 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 43b463a5..7a02985b 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -41,65 +41,194 @@ FILE_LICENCE ( GPL2_OR_LATER ); /****************************************************************************** * - * Registered settings blocks + * Generic settings blocks * ****************************************************************************** */ /** - * Store value of simple setting + * A generic setting * - * @v options DHCP option block + */ +struct generic_setting { + /** List of generic settings */ + struct list_head list; + /** Setting */ + struct setting setting; + /** Size of setting name */ + size_t name_len; + /** Size of setting data */ + size_t data_len; +}; + +/** + * Get generic setting name + * + * @v generic Generic setting + * @ret name Generic setting name + */ +static inline void * generic_setting_name ( struct generic_setting *generic ) { + return ( ( ( void * ) generic ) + sizeof ( *generic ) ); +} + +/** + * Get generic setting data + * + * @v generic Generic setting + * @ret data Generic setting data + */ +static inline void * generic_setting_data ( struct generic_setting *generic ) { + return ( ( ( void * ) generic ) + sizeof ( *generic ) + + generic->name_len ); +} + +/** + * Find generic setting + * + * @v generics Generic settings block + * @v setting Setting to find + * @ret generic Generic setting, or NULL + */ +static struct generic_setting * +find_generic_setting ( struct generic_settings *generics, + struct setting *setting ) { + struct generic_setting *generic; + + list_for_each_entry ( generic, &generics->list, list ) { + if ( setting_cmp ( &generic->setting, setting ) == 0 ) + return generic; + } + return NULL; +} + +/** + * Store value of generic setting + * + * @v settings Settings block * @v setting Setting to store * @v data Setting data, or NULL to clear setting * @v len Length of setting data * @ret rc Return status code */ -int simple_settings_store ( struct settings *settings, struct setting *setting, - const void *data, size_t len ) { - struct simple_settings *simple = - container_of ( settings, struct simple_settings, settings ); - return dhcpopt_extensible_store ( &simple->dhcpopts, setting->tag, - data, len ); +int generic_settings_store ( struct settings *settings, + struct setting *setting, + const void *data, size_t len ) { + struct generic_settings *generics = + container_of ( settings, struct generic_settings, settings ); + struct generic_setting *old; + struct generic_setting *new = NULL; + size_t name_len; + + /* Identify existing generic setting, if any */ + old = find_generic_setting ( generics, setting ); + + /* Create new generic setting, if required */ + if ( len ) { + /* Allocate new generic setting */ + name_len = ( strlen ( setting->name ) + 1 ); + new = zalloc ( sizeof ( *new ) + name_len + len ); + if ( ! new ) + return -ENOMEM; + + /* Populate new generic setting */ + new->name_len = name_len; + new->data_len = len; + memcpy ( &new->setting, setting, sizeof ( new->setting ) ); + new->setting.name = generic_setting_name ( new ); + memcpy ( generic_setting_name ( new ), + setting->name, name_len ); + memcpy ( generic_setting_data ( new ), data, len ); + } + + /* Delete existing generic setting, if any */ + if ( old ) { + list_del ( &old->list ); + free ( old ); + } + + /* Add new setting to list, if any */ + if ( new ) + list_add ( &new->list, &generics->list ); + + return 0; } /** - * Fetch value of simple setting + * Fetch value of generic setting * - * @v options DHCP option block + * @v settings Settings block * @v setting Setting to fetch * @v data Buffer to fill with setting data * @v len Length of buffer * @ret len Length of setting data, or negative error */ -int simple_settings_fetch ( struct settings *settings, struct setting *setting, - void *data, size_t len ) { - struct simple_settings *simple = - container_of ( settings, struct simple_settings, settings ); - return dhcpopt_fetch ( &simple->dhcpopts, setting->tag, data, len ); +int generic_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct generic_settings *generics = + container_of ( settings, struct generic_settings, settings ); + struct generic_setting *generic; + + /* Find generic setting */ + generic = find_generic_setting ( generics, setting ); + if ( ! generic ) + return -ENOENT; + + /* Copy out generic setting data */ + if ( len > generic->data_len ) + len = generic->data_len; + memcpy ( data, generic_setting_data ( generic ), len ); + return generic->data_len; } -/** Simple settings operations */ -struct settings_operations simple_settings_operations = { - .store = simple_settings_store, - .fetch = simple_settings_fetch, +/** + * Clear generic settings block + * + * @v settings Settings block + */ +void generic_settings_clear ( struct settings *settings ) { + struct generic_settings *generics = + container_of ( settings, struct generic_settings, settings ); + struct generic_setting *generic; + struct generic_setting *tmp; + + list_for_each_entry_safe ( generic, tmp, &generics->list, list ) { + list_del ( &generic->list ); + free ( generic ); + } + assert ( list_empty ( &generics->list ) ); +} + +/** Generic settings operations */ +struct settings_operations generic_settings_operations = { + .store = generic_settings_store, + .fetch = generic_settings_fetch, + .clear = generic_settings_clear, }; -/** Root simple settings block */ -struct simple_settings simple_settings_root = { +/****************************************************************************** + * + * Registered settings blocks + * + ****************************************************************************** + */ + +/** Root generic settings block */ +struct generic_settings generic_settings_root = { .settings = { .refcnt = NULL, .name = "", .siblings = - LIST_HEAD_INIT ( simple_settings_root.settings.siblings ), + LIST_HEAD_INIT ( generic_settings_root.settings.siblings ), .children = - LIST_HEAD_INIT ( simple_settings_root.settings.children ), - .op = &simple_settings_operations, + LIST_HEAD_INIT ( generic_settings_root.settings.children ), + .op = &generic_settings_operations, }, + .list = LIST_HEAD_INIT ( generic_settings_root.list ), }; /** Root settings block */ -#define settings_root simple_settings_root.settings +#define settings_root generic_settings_root.settings /** * Find child named settings block @@ -135,7 +264,7 @@ static struct settings * find_child_settings ( struct settings *parent, static struct settings * autovivify_child_settings ( struct settings *parent, const char *name ) { struct { - struct simple_settings simple; + struct generic_settings generic; char name[ strlen ( name ) + 1 /* NUL */ ]; } *new_child; struct settings *settings; @@ -144,7 +273,7 @@ static struct settings * autovivify_child_settings ( struct settings *parent, if ( ( settings = find_child_settings ( parent, name ) ) != NULL ) return settings; - /* Create new simple settings block */ + /* Create new generic settings block */ new_child = zalloc ( sizeof ( *new_child ) ); if ( ! new_child ) { DBGC ( parent, "Settings %p could not create child %s\n", @@ -152,12 +281,31 @@ static struct settings * autovivify_child_settings ( struct settings *parent, return NULL; } memcpy ( new_child->name, name, sizeof ( new_child->name ) ); - simple_settings_init ( &new_child->simple, NULL, new_child->name ); - settings = &new_child->simple.settings; + generic_settings_init ( &new_child->generic, NULL, new_child->name ); + settings = &new_child->generic.settings; register_settings ( settings, parent ); return settings; } +/** + * Return settings block name (for debug only) + * + * @v settings Settings block + * @ret name Settings block name + */ +static const char * settings_name ( struct settings *settings ) { + static char buf[64]; + char tmp[ sizeof ( buf ) ]; + int count; + + for ( count = 0 ; settings ; settings = settings->parent ) { + memcpy ( tmp, buf, sizeof ( tmp ) ); + snprintf ( buf, sizeof ( buf ), "%s%c%s", settings->name, + ( count++ ? '.' : '\0' ), tmp ); + } + return ( buf + 1 ); +} + /** * Parse settings block name * @@ -284,7 +432,8 @@ int register_settings ( struct settings *settings, struct settings *parent ) { ref_get ( parent->refcnt ); settings->parent = parent; list_add_tail ( &settings->siblings, &parent->children ); - DBGC ( settings, "Settings %p registered\n", settings ); + DBGC ( settings, "Settings %p (\"%s\") registered\n", + settings, settings_name ( settings ) ); /* Fix up settings priority */ reprioritise_settings ( settings ); @@ -302,12 +451,14 @@ int register_settings ( struct settings *settings, struct settings *parent ) { */ void unregister_settings ( struct settings *settings ) { + DBGC ( settings, "Settings %p (\"%s\") unregistered\n", + settings, settings_name ( settings ) ); + /* Remove from list of settings */ ref_put ( settings->refcnt ); ref_put ( settings->parent->refcnt ); settings->parent = NULL; list_del ( &settings->siblings ); - DBGC ( settings, "Settings %p unregistered\n", settings ); /* Apply potentially-updated settings */ apply_settings(); @@ -337,6 +488,10 @@ int store_setting ( struct settings *settings, struct setting *setting, if ( ! settings ) settings = &settings_root; + /* Sanity check */ + if ( ! settings->op->store ) + return -ENOTSUP; + /* Store setting */ if ( ( rc = settings->op->store ( settings, setting, data, len ) ) != 0 ) @@ -384,6 +539,10 @@ int fetch_setting ( struct settings *settings, struct setting *setting, if ( ! settings ) settings = &settings_root; + /* Sanity check */ + if ( ! settings->op->fetch ) + return -ENOTSUP; + /* Try this block first */ if ( ( ret = settings->op->fetch ( settings, setting, data, len ) ) >= 0 ) @@ -599,6 +758,16 @@ int fetch_uuid_setting ( struct settings *settings, struct setting *setting, return len; } +/** + * Clear settings block + * + * @v settings Settings block + */ +void clear_settings ( struct settings *settings ) { + if ( settings->op->clear ) + settings->op->clear ( settings ); +} + /** * Compare two settings * @@ -662,6 +831,26 @@ static struct setting * find_setting ( const char *name ) { return NULL; } +/** + * Parse setting name as tag number + * + * @v name Name + * @ret tag Tag number, or 0 if not a valid number + */ +static unsigned int parse_setting_tag ( const char *name ) { + char *tmp = ( ( char * ) name ); + unsigned int tag = 0; + + while ( 1 ) { + tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) ); + if ( *tmp == 0 ) + return tag; + if ( *tmp != '.' ) + return 0; + tmp++; + } +} + /** * Find setting type * @@ -685,31 +874,35 @@ static struct setting_type * find_setting_type ( const char *name ) { * @v get_child Function to find or create child settings block * @v settings Settings block to fill in * @v setting Setting to fill in + * @v tmp_name Buffer for copy of setting name * @ret rc Return status code * * Interprets a name of the form * "[settings_name/]tag_name[:type_name]" and fills in the appropriate * fields. + * + * The @c tmp_name buffer must be large enough to hold a copy of the + * setting name. */ static int parse_setting_name ( const char *name, struct settings * ( * get_child ) ( struct settings *, const char * ), - struct settings **settings, struct setting *setting ) { - char tmp_name[ strlen ( name ) + 1 ]; + struct settings **settings, struct setting *setting, + char *tmp_name ) { char *settings_name; char *setting_name; char *type_name; struct setting *named_setting; - char *tmp; /* Set defaults */ *settings = &settings_root; memset ( setting, 0, sizeof ( *setting ) ); - setting->type = &setting_type_hex; + setting->name = ""; + setting->type = &setting_type_string; /* Split name into "[settings_name/]setting_name[:type_name]" */ - memcpy ( tmp_name, name, sizeof ( tmp_name ) ); + strcpy ( tmp_name, name ); if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) { *(setting_name++) = 0; settings_name = tmp_name; @@ -730,25 +923,16 @@ parse_setting_name ( const char *name, } } - /* Identify tag number */ + /* Identify setting */ if ( ( named_setting = find_setting ( setting_name ) ) != NULL ) { + /* Matches a defined named setting; use that setting */ memcpy ( setting, named_setting, sizeof ( *setting ) ); - } else { - /* Unrecognised name: try to interpret as a tag number */ - tmp = setting_name; - while ( 1 ) { - setting->tag = ( ( setting->tag << 8 ) | - strtoul ( tmp, &tmp, 0 ) ); - if ( *tmp == 0 ) - break; - if ( *tmp != '.' ) { - DBG ( "Invalid setting \"%s\" in \"%s\"\n", - setting_name, name ); - return -ENOENT; - } - tmp++; - } + } else if ( ( setting->tag = parse_setting_tag ( setting_name ) ) !=0){ + /* Is a valid numeric tag; use the tag */ setting->tag |= (*settings)->tag_magic; + } else { + /* Use the arbitrary name */ + setting->name = setting_name; } /* Identify setting type, if specified */ @@ -774,10 +958,11 @@ parse_setting_name ( const char *name, int storef_named_setting ( const char *name, const char *value ) { struct settings *settings; struct setting setting; + char tmp_name[ strlen ( name ) + 1 ]; int rc; if ( ( rc = parse_setting_name ( name, autovivify_child_settings, - &settings, &setting ) ) != 0 ) + &settings, &setting, tmp_name )) != 0) return rc; return storef_setting ( settings, &setting, value ); } @@ -793,10 +978,11 @@ int storef_named_setting ( const char *name, const char *value ) { int fetchf_named_setting ( const char *name, char *buf, size_t len ) { struct settings *settings; struct setting setting; + char tmp_name[ strlen ( name ) + 1 ]; int rc; if ( ( rc = parse_setting_name ( name, find_child_settings, - &settings, &setting ) ) != 0 ) + &settings, &setting, tmp_name )) != 0) return rc; return fetchf_setting ( settings, &setting, buf, len ); } diff --git a/src/include/gpxe/netdevice.h b/src/include/gpxe/netdevice.h index a2d1e692..a2126fac 100644 --- a/src/include/gpxe/netdevice.h +++ b/src/include/gpxe/netdevice.h @@ -269,7 +269,7 @@ struct net_device { struct net_device_stats rx_stats; /** Configuration settings applicable to this device */ - struct simple_settings settings; + struct generic_settings settings; /** Driver private data */ void *priv; @@ -295,6 +295,7 @@ struct net_device { extern struct list_head net_devices; extern struct net_device_operations null_netdev_operations; +extern struct settings_operations netdev_settings_operations; /** * Initialise a network device @@ -385,6 +386,20 @@ netdev_settings ( struct net_device *netdev ) { return &netdev->settings.settings; } +/** + * Initialise a per-netdevice configuration settings block + * + * @v generics Generic settings block + * @v refcnt Containing object reference counter, or NULL + * @v name Settings block name + */ +static inline __attribute__ (( always_inline )) void +netdev_settings_init ( struct net_device *netdev ) { + generic_settings_init ( &netdev->settings, + &netdev->refcnt, netdev->name ); + netdev->settings.settings.op = &netdev_settings_operations; +} + /** * Mark network device as having link up * @@ -440,8 +455,6 @@ extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, extern int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, uint16_t net_proto, const void *ll_source ); -extern struct settings_operations netdev_settings_operations; - /** * Complete network transmission * diff --git a/src/include/gpxe/settings.h b/src/include/gpxe/settings.h index ed3f1590..09934b64 100644 --- a/src/include/gpxe/settings.h +++ b/src/include/gpxe/settings.h @@ -13,7 +13,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include struct settings; struct in_addr; @@ -69,6 +68,11 @@ struct settings_operations { */ int ( * fetch ) ( struct settings *settings, struct setting *setting, void *data, size_t len ); + /** Clear settings block + * + * @v settings Settings block + */ + void ( * clear ) ( struct settings *settings ); }; /** A settings block */ @@ -154,23 +158,24 @@ struct settings_applicator { #define __settings_applicator __table_entry ( SETTINGS_APPLICATORS, 01 ) /** - * A simple settings block + * A generic settings block * */ -struct simple_settings { +struct generic_settings { /** Settings block */ struct settings settings; - /** DHCP options */ - struct dhcp_options dhcpopts; + /** List of generic settings */ + struct list_head list; }; -extern struct settings_operations simple_settings_operations; -extern int simple_settings_store ( struct settings *settings, - struct setting *setting, - const void *data, size_t len ); -extern int simple_settings_fetch ( struct settings *settings, - struct setting *setting, - void *data, size_t len ); +extern struct settings_operations generic_settings_operations; +extern int generic_settings_store ( struct settings *settings, + struct setting *setting, + const void *data, size_t len ); +extern int generic_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ); +extern void generic_settings_clear ( struct settings *settings ); extern int register_settings ( struct settings *settings, struct settings *parent ); @@ -201,6 +206,7 @@ extern unsigned long fetch_uintz_setting ( struct settings *settings, struct setting *setting ); extern int fetch_uuid_setting ( struct settings *settings, struct setting *setting, union uuid *uuid ); +extern void clear_settings ( struct settings *settings ); extern int setting_cmp ( struct setting *a, struct setting *b ); extern struct settings * find_settings ( const char *name ); @@ -263,15 +269,16 @@ static inline void settings_init ( struct settings *settings, /** * Initialise a settings block * - * @v simple Simple settings block + * @v generics Generic settings block * @v refcnt Containing object reference counter, or NULL * @v name Settings block name */ -static inline void simple_settings_init ( struct simple_settings *simple, - struct refcnt *refcnt, - const char *name ) { - settings_init ( &simple->settings, &simple_settings_operations, +static inline void generic_settings_init ( struct generic_settings *generics, + struct refcnt *refcnt, + const char *name ) { + settings_init ( &generics->settings, &generic_settings_operations, refcnt, name, 0 ); + INIT_LIST_HEAD ( &generics->list ); } /** diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c index 2235d499..1c965646 100644 --- a/src/interface/smbios/smbios_settings.c +++ b/src/interface/smbios/smbios_settings.c @@ -63,22 +63,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); ( (_type) << 16 ) | \ ( offsetof ( _structure, _field ) << 8 ) ) -/** - * Store value of SMBIOS setting - * - * @v settings Settings block - * @v setting Setting to store - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -static int smbios_store ( struct settings *settings __unused, - struct setting *setting __unused, - const void *data __unused, size_t len __unused ) { - /* Cannot write data into SMBIOS */ - return -ENOTSUP; -} - /** * Fetch value of SMBIOS setting * @@ -135,7 +119,6 @@ static int smbios_fetch ( struct settings *settings __unused, /** SMBIOS settings operations */ static struct settings_operations smbios_settings_operations = { - .store = smbios_store, .fetch = smbios_fetch, }; diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index 54ce2fbc..b9220f5e 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -56,9 +56,9 @@ static int netdev_store ( struct settings *settings, struct setting *setting, return -EINVAL; memcpy ( netdev->ll_addr, data, len ); return 0; - } else { - return simple_settings_store ( settings, setting, data, len ); } + + return generic_settings_store ( settings, setting, data, len ); } /** @@ -80,13 +80,23 @@ static int netdev_fetch ( struct settings *settings, struct setting *setting, len = netdev->ll_protocol->ll_addr_len; memcpy ( data, netdev->ll_addr, len ); return netdev->ll_protocol->ll_addr_len; - } else { - return simple_settings_fetch ( settings, setting, data, len ); } + + return generic_settings_fetch ( settings, setting, data, len ); +} + +/** + * Clear network device settings + * + * @v settings Settings block + */ +static void netdev_clear ( struct settings *settings ) { + generic_settings_clear ( settings ); } /** Network device configuration settings operations */ struct settings_operations netdev_settings_operations = { .store = netdev_store, .fetch = netdev_fetch, + .clear = netdev_clear, }; diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 1a68af2c..77edbcd3 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -282,6 +282,7 @@ static void free_netdev ( struct refcnt *refcnt ) { netdev_tx_flush ( netdev ); netdev_rx_flush ( netdev ); + clear_settings ( netdev_settings ( netdev ) ); free ( netdev ); } @@ -303,9 +304,7 @@ struct net_device * alloc_netdev ( size_t priv_size ) { netdev->refcnt.free = free_netdev; INIT_LIST_HEAD ( &netdev->tx_queue ); INIT_LIST_HEAD ( &netdev->rx_queue ); - settings_init ( netdev_settings ( netdev ), - &netdev_settings_operations, &netdev->refcnt, - netdev->name, 0 ); + netdev_settings_init ( netdev ); netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) ); } return netdev;