X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=blobdiff_plain;f=discover%2Fplatform-powerpc.c;h=eb54c6dddf231d73a5f1f1965f76703f7967d471;hp=280793425198a483ecea8c1e4217b9844728d842;hb=53aa247c013afcab0dd003fbb7bd056bebcb23e8;hpb=0dae90626e54e46372c9feddc2f9ac82f046547d diff --git a/discover/platform-powerpc.c b/discover/platform-powerpc.c index 2807934..eb54c6d 100644 --- a/discover/platform-powerpc.c +++ b/discover/platform-powerpc.c @@ -8,13 +8,16 @@ #include #include #include +#include #include #include #include #include #include +#include +#include "hostboot.h" #include "platform.h" #include "ipmi.h" #include "dt.h" @@ -43,6 +46,7 @@ struct platform_powerpc { bool persistent); int (*set_os_boot_sensor)( struct platform_powerpc *platform); + void (*get_platform_versions)(struct system_info *info); }; static const char *known_params[] = { @@ -55,6 +59,7 @@ static const char *known_params[] = { "petitboot,debug?", "petitboot,write?", "petitboot,snapshots?", + "petitboot,tty", NULL, }; @@ -179,7 +184,7 @@ static int write_nvram(struct platform_powerpc *platform) struct process *process; struct param *param; const char *argv[6]; - int rc; + int rc = 0; argv[0] = "nvram"; argv[1] = "--update-config"; @@ -310,7 +315,7 @@ static int parse_one_interface_config(struct config *config, } else if (!strcmp(tok, "static")) { ifconf->method = CONFIG_METHOD_STATIC; - /* ip/mask, [optional] gateway */ + /* ip/mask, [optional] gateway, [optional] url */ tok = strtok_r(NULL, ",", &saveptr); if (!tok) goto out_err; @@ -323,6 +328,12 @@ static int parse_one_interface_config(struct config *config, talloc_strdup(ifconf, tok); } + tok = strtok_r(NULL, ",", &saveptr); + if (tok) { + ifconf->static_config.url = + talloc_strdup(ifconf, tok); + } + } else { pb_log("Unknown network configuration method %s\n", tok); goto out_err; @@ -438,7 +449,6 @@ static void populate_bootdev_config(struct platform_powerpc *platform, { struct autoboot_option *opt, *new = NULL; char *pos, *end, *old_dev = NULL; - const char delim = ' '; unsigned int n_new = 0; const char *val; bool conflict; @@ -469,11 +479,9 @@ static void populate_bootdev_config(struct platform_powerpc *platform, if (read_bootdev(config, &pos, opt)) { pb_log("bootdev config is in an unknown format " - "(expected uuid:... or mac:...)"); + "(expected uuid:... or mac:...)\n"); talloc_free(opt); - if (strchr(pos, delim)) - continue; - return; + continue; } new = talloc_realloc(config, new, struct autoboot_option, @@ -558,6 +566,10 @@ static void populate_config(struct platform_powerpc *platform, val = get_param(platform, "petitboot,snapshots?"); if (val) config->disable_snapshots = !strcmp(val, "false"); + + val = get_param(platform, "petitboot,tty"); + if (val) + config->boot_tty = talloc_strdup(config, val); } static char *iface_config_str(void *ctx, struct interface_config *config) @@ -578,10 +590,12 @@ static char *iface_config_str(void *ctx, struct interface_config *config) str = talloc_asprintf_append(str, "dhcp"); } else if (config->method == CONFIG_METHOD_STATIC) { - str = talloc_asprintf_append(str, "static,%s%s%s", + str = talloc_asprintf_append(str, "static,%s%s%s%s%s", config->static_config.address, config->static_config.gateway ? "," : "", - config->static_config.gateway ?: ""); + config->static_config.gateway ?: "", + config->static_config.url ? "," : "", + config->static_config.url ?: ""); } return str; } @@ -723,6 +737,9 @@ static int update_config(struct platform_powerpc *platform, val = config->allow_writes ? "true" : "false"; update_string_config(platform, "petitboot,write?", val); + val = config->boot_tty ?: ""; + update_string_config(platform, "petitboot,tty", val); + update_network_config(platform, config); update_bootdev_config(platform, config); @@ -759,9 +776,8 @@ static int read_bootdev_sysparam(const char *name, uint8_t *val) char path[50]; int fd, rc; - strcpy(path, sysparams_dir); - assert(strlen(name) < sizeof(path) - strlen(path)); - strcat(path, name); + assert(strlen(sysparams_dir) + strlen(name) < sizeof(path)); + snprintf(path, sizeof(path), "%s%s", sysparams_dir, name); fd = open(path, O_RDONLY); if (fd < 0) { @@ -795,9 +811,8 @@ static int write_bootdev_sysparam(const char *name, uint8_t val) char path[50]; int fd, rc; - strcpy(path, sysparams_dir); - assert(strlen(name) < sizeof(path) - strlen(path)); - strcat(path, name); + assert(strlen(sysparams_dir) + strlen(name) < sizeof(path)); + snprintf(path, sizeof(path), "%s%s", sysparams_dir, name); fd = open(path, O_WRONLY); if (fd < 0) { @@ -920,6 +935,11 @@ static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform, return -1; } + pb_debug("IPMI get_bootdev response:\n"); + for (int i = 0; i < resp_len; i++) + pb_debug("%x ", resp[i]); + pb_debug("\n"); + if (resp[0] != 0) { pb_log("platform: non-zero completion code %d from IPMI req\n", resp[0]); @@ -944,7 +964,7 @@ static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform, /* check for valid flags */ if (!(resp[3] & 0x80)) { pb_debug("platform: boot flags are invalid, ignoring\n"); - return 0; + return -1; } *persistent = resp[3] & 0x40; @@ -1012,6 +1032,230 @@ static void get_ipmi_bmc_mac(struct platform *p, uint8_t *buf) } pb_debug("\n"); } + +} + +/* + * Retrieve info from the "Get Device ID" IPMI commands. + * See Chapter 20.1 in the IPMIv2 specification. + */ +static void get_ipmi_bmc_versions(struct platform *p, struct system_info *info) +{ + struct platform_powerpc *platform = p->platform_data; + uint16_t resp_len = 16; + uint8_t resp[16], bcd; + uint32_t aux_version; + int i, rc; + + /* Retrieve info from current side */ + rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_APP, + IPMI_CMD_APP_GET_DEVICE_ID, + NULL, 0, + resp, &resp_len, + ipmi_timeout); + + pb_debug("BMC version resp [%d][%d]:\n", rc, resp_len); + if (resp_len > 0) { + for (i = 0; i < resp_len; i++) { + pb_debug(" %x", resp[i]); + } + pb_debug("\n"); + } + + if (rc == 0 && resp_len == 16) { + info->bmc_current = talloc_array(info, char *, 4); + info->n_bmc_current = 4; + + info->bmc_current[0] = talloc_asprintf(info, "Device ID: 0x%x", + resp[1]); + info->bmc_current[1] = talloc_asprintf(info, "Device Rev: 0x%x", + resp[2]); + bcd = resp[4] & 0x0f; + bcd += 10 * (resp[4] >> 4); + memcpy(&aux_version, &resp[12], sizeof(aux_version)); + info->bmc_current[2] = talloc_asprintf(info, + "Firmware version: %u.%02u.%05u", + resp[3], bcd, aux_version); + bcd = resp[5] & 0x0f; + bcd += 10 * (resp[5] >> 4); + info->bmc_current[3] = talloc_asprintf(info, "IPMI version: %u", + bcd); + } else + pb_log("Failed to retrieve Device ID from IPMI\n"); + + /* Retrieve info from golden side */ + memset(resp, 0, sizeof(resp)); + resp_len = 16; + rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_AMI, + IPMI_CMD_APP_GET_DEVICE_ID_GOLDEN, + NULL, 0, + resp, &resp_len, + ipmi_timeout); + + pb_debug("BMC golden resp [%d][%d]:\n", rc, resp_len); + if (resp_len > 0) { + for (i = 0; i < resp_len; i++) { + pb_debug(" %x", resp[i]); + } + pb_debug("\n"); + } + + if (rc == 0 && resp_len == 16) { + info->bmc_golden = talloc_array(info, char *, 4); + info->n_bmc_golden = 4; + + info->bmc_golden[0] = talloc_asprintf(info, "Device ID: 0x%x", + resp[1]); + info->bmc_golden[1] = talloc_asprintf(info, "Device Rev: 0x%x", + resp[2]); + bcd = resp[4] & 0x0f; + bcd += 10 * (resp[4] >> 4); + memcpy(&aux_version, &resp[12], sizeof(aux_version)); + info->bmc_golden[2] = talloc_asprintf(info, + "Firmware version: %u.%02u.%u", + resp[3], bcd, aux_version); + bcd = resp[5] & 0x0f; + bcd += 10 * (resp[5] >> 4); + info->bmc_golden[3] = talloc_asprintf(info, "IPMI version: %u", + bcd); + } else + pb_log("Failed to retrieve Golden Device ID from IPMI\n"); +} + +static void get_ipmi_network_override(struct platform_powerpc *platform, + struct config *config) +{ + uint16_t min_len = 12, resp_len = 53, version; + const uint32_t magic_value = 0x21706221; + uint8_t resp[resp_len]; + uint32_t cookie; + bool persistent; + int i, rc; + uint8_t req[] = { + 0x61, /* parameter selector: OEM section (network) */ + 0x00, /* no set selector */ + 0x00, /* no block selector */ + }; + + rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS, + IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS, + req, sizeof(req), + resp, &resp_len, + ipmi_timeout); + + pb_debug("IPMI net override resp [%d][%d]:\n", rc, resp_len); + if (resp_len > 0) { + for (i = 0; i < resp_len; i++) { + pb_debug(" %02x", resp[i]); + if (i && (i + 1) % 16 == 0 && i != resp_len - 1) + pb_debug("\n"); + else if (i && (i + 1) % 8 == 0) + pb_debug(" "); + } + pb_debug("\n"); + } + + if (rc) { + pb_debug("IPMI network config option unavailable\n"); + return; + } + + if (resp_len < min_len) { + pb_debug("IPMI net response too small\n"); + return; + } + + if (resp[0] != 0) { + pb_log("platform: non-zero completion code %d from IPMI network req\n", + resp[0]); + return; + } + + /* Check for correct parameter version */ + if ((resp[1] & 0xf) != 0x1) { + pb_log("platform: unexpected version (0x%x) in network override response\n", + resp[0]); + return; + } + + /* Check that the parameters are valid */ + if (resp[2] & 0x80) { + pb_debug("platform: network override is invalid/locked\n"); + return; + } + + /* Check for valid parameters in the boot flags section */ + if (!(resp[3] & 0x80)) { + pb_debug("platform: network override valid flag not set\n"); + return; + } + /* Read the persistent flag; if it is set we need to save this config */ + persistent = resp[3] & 0x40; + if (persistent) + pb_debug("platform: network override is persistent\n"); + + /* Check 4-byte cookie value */ + i = 4; + memcpy(&cookie, &resp[i], sizeof(cookie)); + cookie = __be32_to_cpu(cookie); + if (cookie != magic_value) { + pb_log("%s: Incorrect cookie %x\n", __func__, cookie); + return; + } + i += sizeof(cookie); + + /* Check 2-byte version number */ + memcpy(&version, &resp[i], sizeof(version)); + version = __be16_to_cpu(version); + i += sizeof(version); + if (version != 1) { + pb_debug("Unexpected version: %u\n", version); + return; + } + + /* Interpret the rest of the interface config */ + rc = parse_ipmi_interface_override(config, &resp[i], resp_len - i); + + if (!rc && persistent) { + /* Write this new config to NVRAM */ + update_network_config(platform, config); + rc = write_nvram(platform); + if (rc) + pb_log("platform: Failed to save persistent interface override\n"); + } +} + +static void get_active_consoles(struct config *config) +{ + struct stat sbuf; + char *fsp_prop = NULL; + + config->n_tty = 2; + config->tty_list = talloc_array(config, char *, config->n_tty); + if (!config->tty_list) + goto err; + + config->tty_list[0] = talloc_asprintf(config->tty_list, + "/dev/hvc0 [IPMI / Serial]"); + config->tty_list[1] = talloc_asprintf(config->tty_list, + "/dev/tty1 [VGA]"); + + fsp_prop = talloc_asprintf(config, "%sfsps", devtree_dir); + if (stat(fsp_prop, &sbuf) == 0) { + /* FSP based machines also have a separate serial console */ + config->tty_list = talloc_realloc(config, config->tty_list, + char *, config->n_tty + 1); + if (!config->tty_list) + goto err; + config->tty_list[config->n_tty++] = talloc_asprintf( + config->tty_list, + "/dev/hvc1 [Serial]"); + } + + return; +err: + config->n_tty = 0; + pb_log("Failed to allocate memory for tty_list\n"); } static int load_config(struct platform *p, struct config *config) @@ -1027,7 +1271,7 @@ static int load_config(struct platform *p, struct config *config) if (platform->get_ipmi_bootdev) { bool bootdev_persistent; - uint8_t bootdev; + uint8_t bootdev = IPMI_BOOTDEV_INVALID; rc = platform->get_ipmi_bootdev(platform, &bootdev, &bootdev_persistent); if (!rc && ipmi_bootdev_is_valid(bootdev)) { @@ -1035,6 +1279,11 @@ static int load_config(struct platform *p, struct config *config) } } + if (platform->ipmi) + get_ipmi_network_override(platform, config); + + get_active_consoles(config); + return 0; } @@ -1086,6 +1335,12 @@ static int get_sysinfo(struct platform *p, struct system_info *sysinfo) if (platform->ipmi) get_ipmi_bmc_mac(p, sysinfo->bmc_mac); + if (platform->ipmi) + get_ipmi_bmc_versions(p, sysinfo); + + if (platform->get_platform_versions) + platform->get_platform_versions(sysinfo); + return 0; } @@ -1123,6 +1378,10 @@ static bool probe(struct platform *p, void *ctx) pb_log("platform: no IPMI parameter support\n"); } + rc = stat("/proc/device-tree/bmc", &statbuf); + if (!rc) + platform->get_platform_versions = hostboot_load_versions; + return true; }