summaryrefslogtreecommitdiffstats
path: root/discover
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2014-12-11 16:38:21 +0800
committerJeremy Kerr <jk@ozlabs.org>2014-12-15 15:47:40 +0800
commitd3e95283e69d3099fe9f36e56be1f645f565c843 (patch)
tree42e2f2c71ede42c9922a327b26340b5909f65b5a /discover
parentf69fabee4a120cebaeff2359350e62bd960088f4 (diff)
downloadtalos-petitboot-d3e95283e69d3099fe9f36e56be1f645f565c843.tar.gz
talos-petitboot-d3e95283e69d3099fe9f36e56be1f645f565c843.zip
discover: Integrate ipmi bootdev settings into the priority system
Currently, we expose the boot device priorities through an array in struct config, which will either be the default (network -> disk), or a single device type specified by the IPMI code. Rather than hide the implementation details in this array, we'd like to expose the details of the machine configuration instead. This allows user visibility of the real boot configuration (for example, if an IPMI boot preference is set). This change removes the priority array, and replaces it with the ipmi_bootdev data (and a persistent flag). We update the default-conflict-resolution code to reflect the priorities between IPMI and UUID preferences. Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Diffstat (limited to 'discover')
-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
4 files changed, 151 insertions, 68 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;
OpenPOWER on IntegriCloud