]> git.ozlabs.org Git - petitboot/blobdiff - discover/platform-powerpc.c
discover: Respect persistent flag for network overrides
[petitboot] / discover / platform-powerpc.c
index 7370d7dcc20a21e78dacc57e2efc8ebe7e3cdb0f..b426df644a51452a379fe8ec1735634c844f3518 100644 (file)
@@ -8,13 +8,16 @@
 #include <sys/wait.h>
 #include <sys/fcntl.h>
 #include <sys/stat.h>
+#include <asm/byteorder.h>
 
 #include <file/file.h>
 #include <talloc/talloc.h>
 #include <list/list.h>
 #include <log/log.h>
 #include <process/process.h>
+#include <types/types.h>
 
+#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[] = {
@@ -310,7 +314,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 +327,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;
@@ -575,10 +585,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;
 }
@@ -915,6 +927,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]);
@@ -1007,6 +1024,197 @@ 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.%u",
+                                               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 int load_config(struct platform *p, struct config *config)
@@ -1030,6 +1238,9 @@ static int load_config(struct platform *p, struct config *config)
                }
        }
 
+       if (platform->ipmi)
+               get_ipmi_network_override(platform, config);
+
        return 0;
 }
 
@@ -1081,6 +1292,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;
 }
 
@@ -1118,6 +1335,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;
 }