summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--discover/device-handler.c121
-rw-r--r--discover/platform-powerpc.c78
-rw-r--r--discover/platform.c17
-rw-r--r--discover/platform.h3
-rw-r--r--lib/pb-config/pb-config.c3
-rw-r--r--lib/pb-protocol/pb-protocol.c14
-rw-r--r--lib/types/types.h6
7 files changed, 173 insertions, 69 deletions
diff --git a/discover/device-handler.c b/discover/device-handler.c
index 64fc9fa..fd6f1d3 100644
--- a/discover/device-handler.c
+++ b/discover/device-handler.c
@@ -36,6 +36,15 @@
#include "boot.h"
#include "udev.h"
#include "network.h"
+#include "ipmi.h"
+
+enum default_priority {
+ DEFAULT_PRIORITY_REMOTE = 1,
+ DEFAULT_PRIORITY_LOCAL_UUID = 2,
+ DEFAULT_PRIORITY_LOCAL_FIRST = 3,
+ DEFAULT_PRIORITY_LOCAL_LAST = 0xfe,
+ DEFAULT_PRIORITY_DISABLED = 0xff,
+};
struct device_handler {
struct discover_server *server;
@@ -54,6 +63,8 @@ struct device_handler {
unsigned int sec_to_boot;
struct discover_boot_option *default_boot_option;
+ int default_boot_option_priority;
+
struct list unresolved_boot_options;
struct boot_task *pending_boot;
@@ -429,75 +440,118 @@ static int default_timeout(void *arg)
return 0;
}
-static bool priority_match(struct boot_priority *prio,
+struct {
+ enum ipmi_bootdev ipmi_type;
+ enum device_type device_type;
+} device_type_map[] = {
+ { IPMI_BOOTDEV_NETWORK, DEVICE_TYPE_NETWORK },
+ { IPMI_BOOTDEV_DISK, DEVICE_TYPE_DISK },
+ { IPMI_BOOTDEV_CDROM, DEVICE_TYPE_OPTICAL },
+};
+
+static bool ipmi_device_type_matches(enum ipmi_bootdev ipmi_type,
+ enum device_type device_type)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(device_type_map); i++) {
+ if (device_type_map[i].device_type == device_type)
+ return device_type_map[i].ipmi_type == ipmi_type;
+ }
+
+ return false;
+}
+
+static bool priority_matches(struct boot_priority *prio,
struct discover_boot_option *opt)
{
return prio->type == opt->device->device->type ||
prio->type == DEVICE_TYPE_ANY;
}
-static int default_option_priority(struct discover_boot_option *opt)
+/*
+ * We have different priorities to resolve conflicts between boot options that
+ * report to be the default for their device. This function assigns a priority
+ * for these options.
+ */
+static enum default_priority default_option_priority(
+ struct discover_boot_option *opt)
{
const struct config *config;
- struct boot_priority *prio;
+ const char *dev_str;
unsigned int i;
config = config_get();
- for (i = 0; i < config->n_boot_priorities; i++) {
- prio = &config->boot_priorities[i];
- if (priority_match(prio, opt))
- return prio->priority;
+ /* We give highest priority to IPMI-configured boot options. If
+ * we have an IPMI bootdev configuration set, then we don't allow
+ * any other defaults */
+ if (config->ipmi_bootdev) {
+ bool ipmi_match = ipmi_device_type_matches(config->ipmi_bootdev,
+ opt->device->device->type);
+ if (ipmi_match)
+ return DEFAULT_PRIORITY_REMOTE;
+
+ pb_debug("handler: disabled default priority due to "
+ "non-matching IPMI type %x\n",
+ config->ipmi_bootdev);
+ return DEFAULT_PRIORITY_DISABLED;
}
- return 0;
-}
-
-static bool device_allows_default(struct discover_device *dev)
-{
- const char *dev_str;
-
- dev_str = config_get()->boot_device;
+ /* Next, allow matching by device UUID. If we have one set but it
+ * doesn't match, disallow the default entirely */
+ dev_str = config->boot_device;
+ if (dev_str && dev_str[0]) {
+ if (!strcmp(opt->device->uuid, dev_str))
+ return DEFAULT_PRIORITY_LOCAL_UUID;
- if (!dev_str || !strlen(dev_str))
- return true;
+ pb_debug("handler: disabled default priority due to "
+ "non-matching UUID\n");
+ return DEFAULT_PRIORITY_DISABLED;
+ }
- /* default devices are specified by UUIDs at present */
- if (strcmp(dev->uuid, dev_str))
- return false;
+ /* Lastly, use the local priorities */
+ for (i = 0; i < config->n_boot_priorities; i++) {
+ struct boot_priority *prio = &config->boot_priorities[i];
+ if (priority_matches(prio, opt))
+ return DEFAULT_PRIORITY_LOCAL_FIRST + prio->priority;
+ }
- return true;
+ return DEFAULT_PRIORITY_DISABLED;
}
static void set_default(struct device_handler *handler,
struct discover_boot_option *opt)
{
- int new_prio;
+ enum default_priority cur_prio, new_prio;
if (!handler->autoboot_enabled)
return;
- /* do we allow default-booting from this device? */
- if (!device_allows_default(opt->device))
- return;
+ pb_debug("handler: new default option: %s\n", opt->option->id);
new_prio = default_option_priority(opt);
- /* A negative priority indicates that we don't want to boot this device
- * by default */
- if (new_prio < 0)
+ /* Anything outside our range prevents a default boot */
+ if (new_prio >= DEFAULT_PRIORITY_DISABLED)
return;
+ pb_debug("handler: calculated priority %d\n", new_prio);
+
/* Resolve any conflicts: if we have a new default option, it only
* replaces the current if it has a higher priority. */
if (handler->default_boot_option) {
- int cur_prio;
- cur_prio = default_option_priority(
- handler->default_boot_option);
+ cur_prio = handler->default_boot_option_priority;
- if (new_prio > cur_prio) {
+ if (new_prio < cur_prio) {
+ pb_log("handler: new prio %d beats "
+ "old prio %d for %s\n",
+ new_prio, cur_prio,
+ handler->default_boot_option
+ ->option->id);
handler->default_boot_option = opt;
+ handler->default_boot_option_priority = new_prio;
/* extend the timeout a little, so the user sees some
* indication of the change */
handler->sec_to_boot += 2;
@@ -508,8 +562,9 @@ static void set_default(struct device_handler *handler,
handler->sec_to_boot = config_get()->autoboot_timeout_sec;
handler->default_boot_option = opt;
+ handler->default_boot_option_priority = new_prio;
- pb_log("Boot option %s set as default, timeout %u sec.\n",
+ pb_log("handler: boot option %s set as default, timeout %u sec.\n",
opt->option->id, handler->sec_to_boot);
default_timeout(handler);
diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c
index 6ae28f4..5f6772f 100644
--- a/discover/platform-powerpc.c
+++ b/discover/platform-powerpc.c
@@ -30,7 +30,12 @@ struct param {
};
struct platform_powerpc {
- struct list params;
+ struct list params;
+ int (*get_ipmi_bootdev)(
+ struct platform_powerpc *platform,
+ uint8_t *bootdev, bool *persistent);
+ int (*clear_ipmi_bootdev)(
+ struct platform_powerpc *platform);
};
static const char *known_params[] = {
@@ -570,32 +575,17 @@ static int update_config(struct platform_powerpc *platform,
return write_nvram(platform);
}
-static void set_exclusive_devtype(struct config *config,
- enum device_type devtype)
+static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
+ bool persistent)
{
- config->n_boot_priorities = 2;
- config->boot_priorities = talloc_realloc(config,
- config->boot_priorities, struct boot_priority,
- config->n_boot_priorities);
- config->boot_priorities[0].type = devtype;
- config->boot_priorities[0].priority = 0;
- config->boot_priorities[1].type = DEVICE_TYPE_ANY;
- config->boot_priorities[1].priority = -1;
-}
+ config->ipmi_bootdev = bootdev;
+ config->ipmi_bootdev_persistent = persistent;
-static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev)
-{
switch (bootdev) {
case IPMI_BOOTDEV_NONE:
- break;
case IPMI_BOOTDEV_DISK:
- set_exclusive_devtype(config, DEVICE_TYPE_DISK);
- break;
case IPMI_BOOTDEV_NETWORK:
- set_exclusive_devtype(config, DEVICE_TYPE_NETWORK);
- break;
case IPMI_BOOTDEV_CDROM:
- set_exclusive_devtype(config, DEVICE_TYPE_OPTICAL);
break;
case IPMI_BOOTDEV_SETUP:
config->autoboot_enabled = false;
@@ -684,7 +674,17 @@ static int write_bootdev_sysparam(const char *name, uint8_t val)
return rc;
}
-static void parse_opal_sysparams(struct config *config)
+static int clear_ipmi_bootdev_sysparams(
+ struct platform_powerpc *platform __attribute__((unused)))
+{
+ /* invalidate next-boot-device setting */
+ write_bootdev_sysparam("next-boot-device", 0xff);
+ return 0;
+}
+
+static int get_ipmi_bootdev_sysparams(
+ struct platform_powerpc *platform __attribute__((unused)),
+ uint8_t *bootdev, bool *persistent)
{
uint8_t next_bootdev, default_bootdev;
bool next_valid, default_valid;
@@ -698,12 +698,11 @@ static void parse_opal_sysparams(struct config *config)
/* nothing valid? no need to change the config */
if (!next_valid && !default_valid)
- return;
-
- if (!next_valid)
- next_bootdev = default_bootdev;
+ return -1;
- set_ipmi_bootdev(config, next_bootdev);
+ *persistent = !next_valid;
+ *bootdev = next_valid ? next_bootdev : default_bootdev;
+ return 0;
}
static int load_config(struct platform *p, struct config *config)
@@ -717,7 +716,15 @@ static int load_config(struct platform *p, struct config *config)
populate_config(platform, config);
- parse_opal_sysparams(config);
+ if (platform->get_ipmi_bootdev) {
+ bool bootdev_persistent;
+ uint8_t bootdev;
+ rc = platform->get_ipmi_bootdev(platform, &bootdev,
+ &bootdev_persistent);
+ if (!rc && ipmi_bootdev_is_valid(bootdev)) {
+ set_ipmi_bootdev(config, bootdev, bootdev_persistent);
+ }
+ }
return 0;
}
@@ -737,10 +744,12 @@ static int save_config(struct platform *p, struct config *config)
return rc;
}
-static void finalise_config(struct platform *platform __attribute__((unused)))
+static void finalise_config(struct platform *p, const struct config *config)
{
- /* invalidate next-boot-device setting */
- write_bootdev_sysparam("next-boot-device", 0xff);
+ struct platform_powerpc *platform = to_platform_powerpc(p);
+
+ if (config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev)
+ platform->clear_ipmi_bootdev(platform);
}
static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
@@ -782,6 +791,15 @@ static bool probe(struct platform *p, void *ctx)
list_init(&platform->params);
p->platform_data = platform;
+
+ if (!stat(sysparams_dir, &statbuf)) {
+ pb_debug("platform: using sysparams for IPMI paramters\n");
+ platform->get_ipmi_bootdev = get_ipmi_bootdev_sysparams;
+ platform->clear_ipmi_bootdev = clear_ipmi_bootdev_sysparams;
+
+ } else {
+ pb_log("platform: no IPMI parameter support\n");
+ }
return true;
}
diff --git a/discover/platform.c b/discover/platform.c
index 0a221e2..7275a5f 100644
--- a/discover/platform.c
+++ b/discover/platform.c
@@ -91,6 +91,10 @@ static void dump_config(struct config *config)
prio->priority);
}
+ pb_log(" IPMI boot device 0x%02x%s\n", config->ipmi_bootdev,
+ config->ipmi_bootdev_persistent ? " (persistent)" : "");
+
+
pb_log(" language: %s\n", config->lang ?: "");
}
@@ -130,10 +134,13 @@ void config_set_defaults(struct config *config)
config->boot_priorities = talloc_array(config, struct boot_priority,
config->n_boot_priorities);
config->boot_priorities[0].type = DEVICE_TYPE_NETWORK;
- config->boot_priorities[0].priority = 2;
- config->boot_priorities[1].type = DEVICE_TYPE_DISK;
+ config->boot_priorities[0].priority = 0;
+ config->boot_priorities[1].type = DEVICE_TYPE_ANY;
config->boot_priorities[1].priority = 1;
+ config->ipmi_bootdev = 0;
+ config->ipmi_bootdev_persistent = false;
+
config->debug = config_debug_on_cmdline();
}
@@ -175,8 +182,10 @@ const struct platform *platform_get(void)
void platform_finalise_config(void)
{
- if (platform && platform->finalise_config)
- platform->finalise_config(platform);
+ const struct config *config = config_get();
+
+ if (platform && config && platform->finalise_config)
+ platform->finalise_config(platform, config);
}
int platform_get_sysinfo(struct system_info *info)
diff --git a/discover/platform.h b/discover/platform.h
index 5601b61..ab1bd88 100644
--- a/discover/platform.h
+++ b/discover/platform.h
@@ -8,7 +8,8 @@ struct platform {
bool (*probe)(struct platform *, void *);
int (*load_config)(struct platform *, struct config *);
int (*save_config)(struct platform *, struct config *);
- void (*finalise_config)(struct platform *);
+ void (*finalise_config)(struct platform *,
+ const struct config *);
int (*get_sysinfo)(struct platform *, struct system_info *);
uint16_t dhcp_arch_id;
void *platform_data;
diff --git a/lib/pb-config/pb-config.c b/lib/pb-config/pb-config.c
index fbaa7cb..a2272f4 100644
--- a/lib/pb-config/pb-config.c
+++ b/lib/pb-config/pb-config.c
@@ -74,6 +74,9 @@ struct config *config_copy(void *ctx, const struct config *src)
else
dest->boot_device = NULL;
+ dest->ipmi_bootdev = src->ipmi_bootdev;
+ dest->ipmi_bootdev_persistent = src->ipmi_bootdev_persistent;
+
if (src->lang && strlen(src->lang))
dest->lang = talloc_strdup(dest, src->lang);
else
diff --git a/lib/pb-protocol/pb-protocol.c b/lib/pb-protocol/pb-protocol.c
index 866673d..4398248 100644
--- a/lib/pb-protocol/pb-protocol.c
+++ b/lib/pb-protocol/pb-protocol.c
@@ -284,6 +284,8 @@ int pb_protocol_config_len(const struct config *config)
len += 4 + optional_strlen(config->boot_device);
+ len += 4 + 4; /* ipmi_bootdev, ipmi_bootdev_persistent */
+
len += 4 + optional_strlen(config->lang);
return len;
@@ -488,6 +490,11 @@ int pb_protocol_serialise_config(const struct config *config,
pos += pb_protocol_serialise_string(pos, config->boot_device);
+ *(uint32_t *)pos = __cpu_to_be32(config->ipmi_bootdev);
+ pos += 4;
+ *(uint32_t *)pos = config->ipmi_bootdev_persistent;
+ pos += 4;
+
pos += pb_protocol_serialise_string(pos, config->lang);
assert(pos <= buf + buf_len);
@@ -934,9 +941,14 @@ int pb_protocol_deserialise_config(struct config *config,
if (read_string(config, &pos, &len, &str))
goto out;
-
config->boot_device = str;
+ if (read_u32(&pos, &len, &config->ipmi_bootdev))
+ goto out;
+ if (read_u32(&pos, &len, &tmp))
+ goto out;
+ config->ipmi_bootdev_persistent = !!tmp;
+
if (read_string(config, &pos, &len, &str))
goto out;
diff --git a/lib/types/types.h b/lib/types/types.h
index 25bf556..f543b7f 100644
--- a/lib/types/types.h
+++ b/lib/types/types.h
@@ -122,9 +122,15 @@ struct config {
bool autoboot_enabled;
unsigned int autoboot_timeout_sec;
struct network_config network;
+
struct boot_priority *boot_priorities;
unsigned int n_boot_priorities;
+
char *boot_device;
+
+ unsigned int ipmi_bootdev;
+ bool ipmi_bootdev_persistent;
+
char *lang;
/* not user-settable */
OpenPOWER on IntegriCloud