summaryrefslogtreecommitdiffstats
path: root/discover
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2014-01-29 13:28:06 +0800
committerJeremy Kerr <jk@ozlabs.org>2014-01-30 21:59:10 +0800
commitc14b12980885edd035322cd3bc87efff444c39b1 (patch)
tree138bac8bfacd9d1a36d04421d91e0eedf4164da2 /discover
parentdea7842fb28ff055b4e0f43a6a1fdaf3c4b5ba89 (diff)
downloadtalos-petitboot-c14b12980885edd035322cd3bc87efff444c39b1.tar.gz
talos-petitboot-c14b12980885edd035322cd3bc87efff444c39b1.zip
pb-config: Move config storage modules to "platform" modules in discover code
There's no need to include the config storage code in lib/ as only the discover server should be using it. This change moves the config-storage code to discover/, with the platform-specific parts moved to a 'struct platform'. Each platform has a probe function, which is called during init. The first probe function to return a platform is used. At present we only have the one platform, but it's now non-intrusive to add others. We keep an array of platform pointers in a separate ("platforms") section, to allow the test module to drop-in its own test "platform". Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Diffstat (limited to 'discover')
-rw-r--r--discover/Makefile.am3
-rw-r--r--discover/device-handler.c2
-rw-r--r--discover/discover-server.c1
-rw-r--r--discover/network.c2
-rw-r--r--discover/pb-discover.c6
-rw-r--r--discover/platform-powerpc.c553
-rw-r--r--discover/platform.c157
-rw-r--r--discover/platform.h34
8 files changed, 753 insertions, 5 deletions
diff --git a/discover/Makefile.am b/discover/Makefile.am
index 84c0436..5d5f5b4 100644
--- a/discover/Makefile.am
+++ b/discover/Makefile.am
@@ -54,6 +54,9 @@ pb_discover_SOURCES = \
paths.h \
pb-discover.c \
pb-discover.h \
+ platform.c \
+ platform.h \
+ platform-powerpc.c \
resource.c \
resource.h \
sysinfo.c \
diff --git a/discover/device-handler.c b/discover/device-handler.c
index d3256fa..c57b7b6 100644
--- a/discover/device-handler.c
+++ b/discover/device-handler.c
@@ -8,7 +8,6 @@
#include <sys/stat.h>
#include <sys/wait.h>
-#include <pb-config/pb-config.h>
#include <talloc/talloc.h>
#include <list/list.h>
#include <log/log.h>
@@ -19,6 +18,7 @@
#include "device-handler.h"
#include "discover-server.h"
+#include "platform.h"
#include "event.h"
#include "parser.h"
#include "resource.h"
diff --git a/discover/discover-server.c b/discover/discover-server.c
index 3cf9fac..1765074 100644
--- a/discover/discover-server.c
+++ b/discover/discover-server.c
@@ -20,6 +20,7 @@
#include "device-handler.h"
#include "discover-server.h"
+#include "platform.h"
#include "sysinfo.h"
struct discover_server {
diff --git a/discover/network.c b/discover/network.c
index cf340da..9da0f7a 100644
--- a/discover/network.c
+++ b/discover/network.c
@@ -14,13 +14,13 @@
#include <types/types.h>
#include <talloc/talloc.h>
#include <waiter/waiter.h>
-#include <pb-config/pb-config.h>
#include <process/process.h>
#include <system/system.h>
#include "file.h"
#include "network.h"
#include "sysinfo.h"
+#include "platform.h"
#include "device-handler.h"
#define HWADDR_SIZE 6
diff --git a/discover/pb-discover.c b/discover/pb-discover.c
index d8708a3..bb590a9 100644
--- a/discover/pb-discover.c
+++ b/discover/pb-discover.c
@@ -11,7 +11,6 @@
#include <waiter/waiter.h>
#include <log/log.h>
-#include <pb-config/pb-config.h>
#include <process/process.h>
#include <talloc/talloc.h>
@@ -21,6 +20,7 @@
#include "device-handler.h"
#include "network.h"
#include "sysinfo.h"
+#include "platform.h"
static void print_version(void)
{
@@ -170,7 +170,7 @@ int main(int argc, char *argv[])
if (!procset)
return EXIT_FAILURE;
- config_init(NULL);
+ platform_init(NULL);
if (opts.no_autoboot == opt_yes)
config_set_autoboot(false);
@@ -205,7 +205,7 @@ int main(int argc, char *argv[])
user_event_destroy(uev);
udev_destroy(udev);
discover_server_destroy(server);
- config_fini();
+ platform_fini();
talloc_free(waitset);
pb_log("--- end ---\n");
diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c
new file mode 100644
index 0000000..e2a8631
--- /dev/null
+++ b/discover/platform-powerpc.c
@@ -0,0 +1,553 @@
+
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <talloc/talloc.h>
+#include <list/list.h>
+#include <log/log.h>
+#include <process/process.h>
+
+#include "platform.h"
+
+static const char *partition = "common";
+
+struct param {
+ char *name;
+ char *value;
+ bool modified;
+ struct list_item list;
+};
+
+struct platform_powerpc {
+ struct list params;
+};
+
+static const char *known_params[] = {
+ "auto-boot?",
+ "petitboot,network",
+ "petitboot,timeout",
+ NULL,
+};
+
+#define to_platform_powerpc(p) \
+ (struct platform_powerpc *)(p->platform_data)
+
+/* a partition max a max size of 64k * 16bytes = 1M */
+static const int max_partition_size = 64 * 1024 * 16;
+
+static bool param_is_known(const char *param, unsigned int len)
+{
+ const char *known_param;
+ unsigned int i;
+
+ for (i = 0; known_params[i]; i++) {
+ known_param = known_params[i];
+ if (len == strlen(known_param) &&
+ !strncmp(param, known_param, len))
+ return true;
+ }
+
+ return false;
+}
+
+static int parse_nvram_params(struct platform_powerpc *platform,
+ char *buf, int len)
+{
+ char *pos, *name, *value;
+ unsigned int paramlen;
+ int i, count;
+
+ /* discard 2 header lines:
+ * "common" partiton"
+ * ------------------
+ */
+ pos = buf;
+ count = 0;
+
+ for (i = 0; i < len; i++) {
+ if (pos[i] == '\n')
+ count++;
+ if (count == 2)
+ break;
+ }
+
+ if (i == len) {
+ fprintf(stderr, "failure parsing nvram output\n");
+ return -1;
+ }
+
+ for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
+ unsigned int namelen;
+ struct param *param;
+ char *newline;
+
+ newline = strchr(pos, '\n');
+ if (!newline)
+ break;
+
+ *newline = '\0';
+
+ paramlen = strlen(pos);
+
+ name = pos;
+ value = strchr(pos, '=');
+ if (!value)
+ continue;
+
+ namelen = value - name;
+ if (namelen == 0)
+ continue;
+
+ if (!param_is_known(name, namelen))
+ continue;
+
+ value++;
+
+ param = talloc(platform, struct param);
+ param->modified = false;
+ param->name = talloc_strndup(platform, name, namelen);
+ param->value = talloc_strdup(platform, value);
+ list_add(&platform->params, &param->list);
+ }
+
+ return 0;
+}
+
+static int parse_nvram(struct platform_powerpc *platform)
+{
+ struct process *process;
+ const char *argv[5];
+ int rc;
+
+ argv[0] = "nvram";
+ argv[1] = "--print-config";
+ argv[2] = "--partition";
+ argv[3] = partition;
+ argv[4] = NULL;
+
+ process = process_create(platform);
+ process->path = "nvram";
+ process->argv = argv;
+ process->keep_stdout = true;
+
+ rc = process_run_sync(process);
+
+ if (rc || !process_exit_ok(process)) {
+ fprintf(stderr, "nvram process returned "
+ "non-zero exit status\n");
+ rc = -1;
+ } else {
+ rc = parse_nvram_params(platform, process->stdout_buf,
+ process->stdout_len);
+ }
+
+ process_release(process);
+ return rc;
+}
+
+static int write_nvram(struct platform_powerpc *platform)
+{
+ struct process *process;
+ struct param *param;
+ const char *argv[6];
+ int rc;
+
+ argv[0] = "nvram";
+ argv[1] = "--update-config";
+ argv[2] = NULL;
+ argv[3] = "--partition";
+ argv[4] = partition;
+ argv[5] = NULL;
+
+ process = process_create(platform);
+ process->path = "nvram";
+ process->argv = argv;
+
+ list_for_each_entry(&platform->params, param, list) {
+ char *paramstr;
+
+ if (!param->modified)
+ continue;
+
+ paramstr = talloc_asprintf(platform, "%s=%s",
+ param->name, param->value);
+ argv[2] = paramstr;
+
+ rc = process_run_sync(process);
+
+ talloc_free(paramstr);
+
+ if (rc || !process_exit_ok(process)) {
+ rc = -1;
+ pb_log("nvram update process returned "
+ "non-zero exit status\n");
+ break;
+ }
+ }
+
+ process_release(process);
+ return rc;
+}
+
+static const char *get_param(struct platform_powerpc *platform,
+ const char *name)
+{
+ struct param *param;
+
+ list_for_each_entry(&platform->params, param, list)
+ if (!strcmp(param->name, name))
+ return param->value;
+ return NULL;
+}
+
+static void set_param(struct platform_powerpc *platform, const char *name,
+ const char *value)
+{
+ struct param *param;
+
+ list_for_each_entry(&platform->params, param, list) {
+ if (strcmp(param->name, name))
+ continue;
+
+ if (!strcmp(param->value, value))
+ return;
+
+ talloc_free(param->value);
+ param->value = talloc_strdup(param, value);
+ param->modified = true;
+ return;
+ }
+
+
+ param = talloc(platform, struct param);
+ param->modified = true;
+ param->name = talloc_strdup(platform, name);
+ param->value = talloc_strdup(platform, value);
+ list_add(&platform->params, &param->list);
+}
+
+static int parse_hwaddr(struct interface_config *ifconf, char *str)
+{
+ int i;
+
+ if (strlen(str) != strlen("00:00:00:00:00:00"))
+ return -1;
+
+ for (i = 0; i < HWADDR_SIZE; i++) {
+ char byte[3], *endp;
+ unsigned long x;
+
+ byte[0] = str[i * 3 + 0];
+ byte[1] = str[i * 3 + 1];
+ byte[2] = '\0';
+
+ x = strtoul(byte, &endp, 16);
+ if (endp != byte + 2)
+ return -1;
+
+ ifconf->hwaddr[i] = x & 0xff;
+ }
+
+ return 0;
+}
+
+static int parse_one_interface_config(struct config *config,
+ char *confstr)
+{
+ struct interface_config *ifconf;
+ char *tok, *saveptr;
+
+ ifconf = talloc_zero(config, struct interface_config);
+
+ if (!confstr || !strlen(confstr))
+ goto out_err;
+
+ /* first token should be the mac address */
+ tok = strtok_r(confstr, ",", &saveptr);
+ if (!tok)
+ goto out_err;
+
+ if (parse_hwaddr(ifconf, tok))
+ goto out_err;
+
+ /* second token is the method */
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
+ ifconf->ignore = true;
+
+ } else if (!strcmp(tok, "dhcp")) {
+ ifconf->method = CONFIG_METHOD_DHCP;
+
+ } else if (!strcmp(tok, "static")) {
+ ifconf->method = CONFIG_METHOD_STATIC;
+
+ /* ip/mask, [optional] gateway */
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (!tok)
+ goto out_err;
+ ifconf->static_config.address =
+ talloc_strdup(ifconf, tok);
+
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (tok) {
+ ifconf->static_config.gateway =
+ talloc_strdup(ifconf, tok);
+ }
+
+ } else {
+ pb_log("Unknown network configuration method %s\n", tok);
+ goto out_err;
+ }
+
+ config->network.interfaces = talloc_realloc(config,
+ config->network.interfaces,
+ struct interface_config *,
+ ++config->network.n_interfaces);
+
+ config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
+
+ return 0;
+out_err:
+ talloc_free(ifconf);
+ return -1;
+}
+
+static int parse_one_dns_config(struct config *config,
+ char *confstr)
+{
+ char *tok, *saveptr;
+
+ for (tok = strtok_r(confstr, ",", &saveptr); tok;
+ tok = strtok_r(NULL, ",", &saveptr)) {
+
+ char *server = talloc_strdup(config, tok);
+
+ config->network.dns_servers = talloc_realloc(config,
+ config->network.dns_servers, const char *,
+ ++config->network.n_dns_servers);
+
+ config->network.dns_servers[config->network.n_dns_servers - 1]
+ = server;
+ }
+
+ return 0;
+}
+
+static void populate_network_config(struct platform_powerpc *platform,
+ struct config *config)
+{
+ const char *cval;
+ char *val;
+ int i;
+
+ cval = get_param(platform, "petitboot,network");
+ if (!cval || !strlen(cval))
+ return;
+
+ val = talloc_strdup(config, cval);
+
+ for (i = 0; ; i++) {
+ char *tok, *saveptr;
+
+ tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
+ if (!tok)
+ break;
+
+ if (!strncasecmp(tok, "dns,", strlen("dns,")))
+ parse_one_dns_config(config, tok + strlen("dns,"));
+ else
+ parse_one_interface_config(config, tok);
+
+ }
+
+ talloc_free(val);
+}
+
+static void populate_config(struct platform_powerpc *platform,
+ struct config *config)
+{
+ const char *val;
+ char *end;
+ unsigned long timeout;
+
+ /* if the "auto-boot?' property is present and "false", disable auto
+ * boot */
+ val = get_param(platform, "auto-boot?");
+ config->autoboot_enabled = !val || strcmp(val, "false");
+
+ val = get_param(platform, "petitboot,timeout");
+ if (val) {
+ timeout = strtoul(val, &end, 10);
+ if (end != val) {
+ if (timeout >= INT_MAX)
+ timeout = INT_MAX;
+ config->autoboot_timeout_sec = (int)timeout;
+ }
+ }
+
+ populate_network_config(platform, config);
+}
+
+static char *iface_config_str(void *ctx, struct interface_config *config)
+{
+ char *str;
+
+ /* todo: HWADDR size is hardcoded as 6, but we may need to handle
+ * different hardware address formats */
+ str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
+ config->hwaddr[0], config->hwaddr[1],
+ config->hwaddr[2], config->hwaddr[3],
+ config->hwaddr[4], config->hwaddr[5]);
+
+ if (config->ignore) {
+ str = talloc_asprintf_append(str, "ignore");
+
+ } else if (config->method == CONFIG_METHOD_DHCP) {
+ str = talloc_asprintf_append(str, "dhcp");
+
+ } else if (config->method == CONFIG_METHOD_STATIC) {
+ str = talloc_asprintf_append(str, "static,%s%s%s",
+ config->static_config.address,
+ config->static_config.gateway ? "," : "",
+ config->static_config.gateway ?: "");
+ }
+ return str;
+}
+
+static char *dns_config_str(void *ctx, const char **dns_servers, int n)
+{
+ char *str;
+ int i;
+
+ str = talloc_strdup(ctx, "dns,");
+ for (i = 0; i < n; i++) {
+ str = talloc_asprintf_append(str, "%s%s",
+ i == 0 ? "" : ",",
+ dns_servers[i]);
+ }
+
+ return str;
+}
+
+static void update_string_config(struct platform_powerpc *platform,
+ const char *name, const char *value)
+{
+ const char *cur;
+
+ cur = get_param(platform, name);
+
+ /* don't set an empty parameter if it doesn't already exist */
+ if (!cur && !strlen(value))
+ return;
+
+ set_param(platform, name, value);
+}
+
+static void update_network_config(struct platform_powerpc *platform,
+ struct config *config)
+{
+ unsigned int i;
+ char *val;
+
+ val = talloc_strdup(platform, "");
+
+ for (i = 0; i < config->network.n_interfaces; i++) {
+ char *iface_str = iface_config_str(platform,
+ config->network.interfaces[i]);
+ val = talloc_asprintf_append(val, "%s%s",
+ *val == '\0' ? "" : " ", iface_str);
+ talloc_free(iface_str);
+ }
+
+ if (config->network.n_dns_servers) {
+ char *dns_str = dns_config_str(platform,
+ config->network.dns_servers,
+ config->network.n_dns_servers);
+ val = talloc_asprintf_append(val, "%s%s",
+ *val == '\0' ? "" : " ", dns_str);
+ talloc_free(dns_str);
+ }
+
+ update_string_config(platform, "petitboot,network", val);
+
+ talloc_free(val);
+}
+
+static int update_config(struct platform_powerpc *platform,
+ struct config *config, struct config *defaults)
+{
+ char *tmp = NULL;
+ const char *val;
+
+ if (config->autoboot_enabled == defaults->autoboot_enabled)
+ val = "";
+ else
+ val = config->autoboot_enabled ? "true" : "false";
+ update_string_config(platform, "auto-boot?", val);
+
+ if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
+ val = "";
+ else
+ val = tmp = talloc_asprintf(platform, "%d",
+ config->autoboot_timeout_sec);
+
+ update_string_config(platform, "petitboot,timeout", val);
+ if (tmp)
+ talloc_free(tmp);
+
+ update_network_config(platform, config);
+
+ return write_nvram(platform);
+}
+
+static int load_config(struct platform *p, struct config *config)
+{
+ struct platform_powerpc *platform = to_platform_powerpc(p);
+ int rc;
+
+ rc = parse_nvram(platform);
+ if (rc)
+ return rc;
+
+ populate_config(platform, config);
+
+ return 0;
+}
+
+static int save_config(struct platform *p, struct config *config)
+{
+ struct platform_powerpc *platform = to_platform_powerpc(p);
+ struct config *defaults;
+ int rc;
+
+ defaults = talloc_zero(platform, struct config);
+ config_set_defaults(defaults);
+
+ rc = update_config(platform, config, defaults);
+
+ talloc_free(defaults);
+ return rc;
+}
+
+static bool probe(struct platform *p, void *ctx)
+{
+ struct platform_powerpc *platform;
+
+ platform = talloc(ctx, struct platform_powerpc);
+ list_init(&platform->params);
+
+ p->platform_data = platform;
+ return true;
+}
+
+static struct platform platform_powerpc = {
+ .name = "powerpc",
+ .probe = probe,
+ .load_config = load_config,
+ .save_config = save_config,
+};
+
+register_platform(platform_powerpc);
diff --git a/discover/platform.c b/discover/platform.c
new file mode 100644
index 0000000..66e776e
--- /dev/null
+++ b/discover/platform.c
@@ -0,0 +1,157 @@
+
+#include <string.h>
+
+#include <log/log.h>
+#include <types/types.h>
+#include <talloc/talloc.h>
+
+#include "platform.h"
+
+void *platform_ctx;
+static struct platform *platform;
+static struct config *config;
+
+static void dump_config(struct config *config)
+{
+ unsigned int i;
+
+ pb_log("configuration:\n");
+
+ if (config->autoboot_enabled)
+ pb_log(" autoboot: enabled, %d sec\n",
+ config->autoboot_timeout_sec);
+ else
+ pb_log(" autoboot: disabled\n");
+
+ if (config->network.n_interfaces || config->network.n_dns_servers)
+ pb_log(" network configuration:\n");
+
+ for (i = 0; i < config->network.n_interfaces; i++) {
+ struct interface_config *ifconf =
+ config->network.interfaces[i];
+
+ pb_log(" interface %02x:%02x:%02x:%02x:%02x:%02x\n",
+ ifconf->hwaddr[0], ifconf->hwaddr[1],
+ ifconf->hwaddr[2], ifconf->hwaddr[3],
+ ifconf->hwaddr[4], ifconf->hwaddr[5]);
+
+ if (ifconf->ignore) {
+ pb_log(" ignore\n");
+ continue;
+ }
+
+ if (ifconf->method == CONFIG_METHOD_DHCP) {
+ pb_log(" dhcp\n");
+
+ } else if (ifconf->method == CONFIG_METHOD_STATIC) {
+ pb_log(" static:\n");
+ pb_log(" ip: %s\n", ifconf->static_config.address);
+ pb_log(" gw: %s\n", ifconf->static_config.gateway);
+
+ }
+ }
+ for (i = 0; i < config->network.n_dns_servers; i++)
+ pb_log(" dns server %s\n", config->network.dns_servers[i]);
+}
+
+void config_set_defaults(struct config *config)
+{
+ config->autoboot_enabled = true;
+ config->autoboot_timeout_sec = 10;
+ config->network.interfaces = NULL;
+ config->network.n_interfaces = 0;
+ config->network.dns_servers = NULL;
+ config->network.n_dns_servers = 0;
+
+ config->n_boot_priorities = 2;
+ config->boot_priorities = talloc_array(config, struct boot_priority,
+ config->n_boot_priorities);
+ config->boot_priorities[0].type = DEVICE_TYPE_NETWORK;
+ config->boot_priorities[1].type = DEVICE_TYPE_DISK;
+
+}
+
+int platform_init(void *ctx)
+{
+ extern struct platform *__start_platforms, *__stop_platforms;
+ struct platform **p;
+
+ platform_ctx = talloc_new(ctx);
+
+ for (p = &__start_platforms; p < &__stop_platforms; p++) {
+ if (!(*p)->probe(*p, platform_ctx))
+ continue;
+ platform = *p;
+ break;
+ }
+
+ config = talloc(platform_ctx, struct config);
+ config_set_defaults(config);
+
+ if (platform) {
+ pb_log("Detected platform type: %s\n", platform->name);
+ if (platform->load_config)
+ platform->load_config(platform, config);
+ } else {
+ pb_log("No platform type detected, some platform-specific "
+ "functionality will be disabled\n");
+ }
+
+ dump_config(config);
+
+ return 0;
+}
+
+int config_set(struct config *newconfig)
+{
+ int rc;
+
+ if (!platform || !platform->save_config)
+ return -1;
+
+ if (newconfig == config)
+ return 0;
+
+ pb_log("new configuration data received\n");
+ dump_config(newconfig);
+
+ rc = platform->save_config(platform, newconfig);
+
+ if (!rc)
+ config = talloc_steal(platform_ctx, newconfig);
+ else
+ pb_log("error saving new configuration; changes lost\n");
+
+ return rc;
+}
+
+/* A non-exported function to allow the test infrastructure to initialise
+ * (and change) the configuration variables */
+struct parser_test;
+struct config __attribute__((unused)) *test_config_init(
+ struct parser_test *test);
+struct config *test_config_init(struct parser_test *test)
+{
+ config = talloc(test, struct config);
+ config_set_defaults(config);
+ return config;
+}
+
+const struct config *config_get(void)
+{
+ return config;
+}
+
+void config_set_autoboot(bool autoboot_enabled)
+{
+ config->autoboot_enabled = autoboot_enabled;
+
+ pb_log("set autoboot: %s\n",
+ config->autoboot_enabled ? "enabled" : "disabled");
+}
+
+int platform_fini(void)
+{
+ talloc_free(platform_ctx);
+ return 0;
+}
diff --git a/discover/platform.h b/discover/platform.h
new file mode 100644
index 0000000..44315fa
--- /dev/null
+++ b/discover/platform.h
@@ -0,0 +1,34 @@
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include <types/types.h>
+
+struct platform {
+ const char *name;
+ bool (*probe)(struct platform *, void *);
+ int (*load_config)(struct platform *, struct config *);
+ int (*save_config)(struct platform *, struct config *);
+ void *platform_data;
+};
+
+int platform_init(void *ctx);
+int platform_fini(void);
+
+/* configuration interface */
+const struct config *config_get(void);
+int config_set(struct config *config);
+void config_set_autoboot(bool autoboot_enabled);
+
+/* for use by the platform-specific storage code */
+void config_set_defaults(struct config *config);
+
+#define __platform_ptrname(_n) __platform_ ## _n
+#define _platform_ptrname(_n) __platform_ptrname(_n)
+
+#define register_platform(p) \
+ static __attribute__((section("platforms"))) \
+ __attribute__((used)) \
+ struct platform * _platform_ptrname(__COUNTER__) = &p;
+
+#endif /* PLATFORM_H */
+
OpenPOWER on IntegriCloud