diff options
-rw-r--r-- | discover/device-handler.c | 121 | ||||
-rw-r--r-- | discover/platform-powerpc.c | 78 | ||||
-rw-r--r-- | discover/platform.c | 17 | ||||
-rw-r--r-- | discover/platform.h | 3 | ||||
-rw-r--r-- | lib/pb-config/pb-config.c | 3 | ||||
-rw-r--r-- | lib/pb-protocol/pb-protocol.c | 14 | ||||
-rw-r--r-- | lib/types/types.h | 6 |
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 */ |