12 #include <file/file.h>
13 #include <talloc/talloc.h>
14 #include <list/list.h>
16 #include <process/process.h>
22 static const char *partition = "common";
23 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
24 static const char *devtree_dir = "/proc/device-tree/";
25 static const int ipmi_timeout = 500; /* milliseconds */
31 struct list_item list;
34 struct platform_powerpc {
37 bool ipmi_bootdev_persistent;
38 int (*get_ipmi_bootdev)(
39 struct platform_powerpc *platform,
40 uint8_t *bootdev, bool *persistent);
41 int (*clear_ipmi_bootdev)(
42 struct platform_powerpc *platform,
44 int (*set_os_boot_sensor)(
45 struct platform_powerpc *platform);
48 static const char *known_params[] = {
60 #define to_platform_powerpc(p) \
61 (struct platform_powerpc *)(p->platform_data)
63 /* a partition max a max size of 64k * 16bytes = 1M */
64 static const int max_partition_size = 64 * 1024 * 16;
66 static bool param_is_known(const char *param, unsigned int len)
68 const char *known_param;
71 for (i = 0; known_params[i]; i++) {
72 known_param = known_params[i];
73 if (len == strlen(known_param) &&
74 !strncmp(param, known_param, len))
81 static int parse_nvram_params(struct platform_powerpc *platform,
84 char *pos, *name, *value;
85 unsigned int paramlen;
88 /* discard 2 header lines:
95 for (i = 0; i < len; i++) {
103 fprintf(stderr, "failure parsing nvram output\n");
107 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
108 unsigned int namelen;
112 newline = strchr(pos, '\n');
118 paramlen = strlen(pos);
121 value = strchr(pos, '=');
125 namelen = value - name;
129 if (!param_is_known(name, namelen))
134 param = talloc(platform, struct param);
135 param->modified = false;
136 param->name = talloc_strndup(platform, name, namelen);
137 param->value = talloc_strdup(platform, value);
138 list_add(&platform->params, ¶m->list);
144 static int parse_nvram(struct platform_powerpc *platform)
146 struct process *process;
151 argv[1] = "--print-config";
152 argv[2] = "--partition";
156 process = process_create(platform);
157 process->path = "nvram";
158 process->argv = argv;
159 process->keep_stdout = true;
161 rc = process_run_sync(process);
163 if (rc || !process_exit_ok(process)) {
164 fprintf(stderr, "nvram process returned "
165 "non-zero exit status\n");
168 rc = parse_nvram_params(platform, process->stdout_buf,
169 process->stdout_len);
172 process_release(process);
176 static int write_nvram(struct platform_powerpc *platform)
178 struct process *process;
184 argv[1] = "--update-config";
186 argv[3] = "--partition";
190 process = process_create(platform);
191 process->path = "nvram";
192 process->argv = argv;
194 list_for_each_entry(&platform->params, param, list) {
197 if (!param->modified)
200 paramstr = talloc_asprintf(platform, "%s=%s",
201 param->name, param->value);
204 rc = process_run_sync(process);
206 talloc_free(paramstr);
208 if (rc || !process_exit_ok(process)) {
210 pb_log("nvram update process returned "
211 "non-zero exit status\n");
216 process_release(process);
220 static const char *get_param(struct platform_powerpc *platform,
225 list_for_each_entry(&platform->params, param, list)
226 if (!strcmp(param->name, name))
231 static void set_param(struct platform_powerpc *platform, const char *name,
236 list_for_each_entry(&platform->params, param, list) {
237 if (strcmp(param->name, name))
240 if (!strcmp(param->value, value))
243 talloc_free(param->value);
244 param->value = talloc_strdup(param, value);
245 param->modified = true;
250 param = talloc(platform, struct param);
251 param->modified = true;
252 param->name = talloc_strdup(platform, name);
253 param->value = talloc_strdup(platform, value);
254 list_add(&platform->params, ¶m->list);
257 static int parse_hwaddr(struct interface_config *ifconf, char *str)
261 if (strlen(str) != strlen("00:00:00:00:00:00"))
264 for (i = 0; i < HWADDR_SIZE; i++) {
268 byte[0] = str[i * 3 + 0];
269 byte[1] = str[i * 3 + 1];
272 x = strtoul(byte, &endp, 16);
273 if (endp != byte + 2)
276 ifconf->hwaddr[i] = x & 0xff;
282 static int parse_one_interface_config(struct config *config,
285 struct interface_config *ifconf;
288 ifconf = talloc_zero(config, struct interface_config);
290 if (!confstr || !strlen(confstr))
293 /* first token should be the mac address */
294 tok = strtok_r(confstr, ",", &saveptr);
298 if (parse_hwaddr(ifconf, tok))
301 /* second token is the method */
302 tok = strtok_r(NULL, ",", &saveptr);
303 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
304 ifconf->ignore = true;
306 } else if (!strcmp(tok, "dhcp")) {
307 ifconf->method = CONFIG_METHOD_DHCP;
309 } else if (!strcmp(tok, "static")) {
310 ifconf->method = CONFIG_METHOD_STATIC;
312 /* ip/mask, [optional] gateway */
313 tok = strtok_r(NULL, ",", &saveptr);
316 ifconf->static_config.address =
317 talloc_strdup(ifconf, tok);
319 tok = strtok_r(NULL, ",", &saveptr);
321 ifconf->static_config.gateway =
322 talloc_strdup(ifconf, tok);
326 pb_log("Unknown network configuration method %s\n", tok);
330 config->network.interfaces = talloc_realloc(config,
331 config->network.interfaces,
332 struct interface_config *,
333 ++config->network.n_interfaces);
335 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
343 static int parse_one_dns_config(struct config *config,
346 char *tok, *saveptr = NULL;
348 for (tok = strtok_r(confstr, ",", &saveptr); tok;
349 tok = strtok_r(NULL, ",", &saveptr)) {
351 char *server = talloc_strdup(config, tok);
353 config->network.dns_servers = talloc_realloc(config,
354 config->network.dns_servers, const char *,
355 ++config->network.n_dns_servers);
357 config->network.dns_servers[config->network.n_dns_servers - 1]
364 static void populate_network_config(struct platform_powerpc *platform,
365 struct config *config)
367 char *val, *saveptr = NULL;
371 cval = get_param(platform, "petitboot,network");
372 if (!cval || !strlen(cval))
375 val = talloc_strdup(config, cval);
380 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
384 if (!strncasecmp(tok, "dns,", strlen("dns,")))
385 parse_one_dns_config(config, tok + strlen("dns,"));
387 parse_one_interface_config(config, tok);
394 static int read_bootdev(void *ctx, char **pos, struct autoboot_option *opt)
396 char *delim = strchr(*pos, ' ');
397 int len, prefix = 0, rc = -1;
398 enum device_type type;
400 if (!strncmp(*pos, "uuid:", strlen("uuid:"))) {
401 prefix = strlen("uuid:");
402 opt->boot_type = BOOT_DEVICE_UUID;
404 } else if (!strncmp(*pos, "mac:", strlen("mac:"))) {
405 prefix = strlen("mac:");
406 opt->boot_type = BOOT_DEVICE_UUID;
409 type = find_device_type(*pos);
410 if (type != DEVICE_TYPE_UNKNOWN) {
412 opt->boot_type = BOOT_DEVICE_TYPE;
417 if (opt->boot_type == BOOT_DEVICE_UUID) {
419 len = (int)(delim - *pos) - prefix;
423 opt->uuid = talloc_strndup(ctx, *pos + prefix, len);
426 /* Always advance pointer to next option or end */
430 *pos += strlen(*pos);
435 static void populate_bootdev_config(struct platform_powerpc *platform,
436 struct config *config)
438 struct autoboot_option *opt, *new = NULL;
439 char *pos, *end, *old_dev = NULL;
440 const char delim = ' ';
441 unsigned int n_new = 0;
445 /* Check for old-style bootdev */
446 val = get_param(platform, "petitboot,bootdev");
447 if (val && strlen(val)) {
448 pos = talloc_strdup(config, val);
449 if (!strncmp(val, "uuid:", strlen("uuid:")))
450 old_dev = talloc_strdup(config,
451 val + strlen("uuid:"));
452 else if (!strncmp(val, "mac:", strlen("mac:")))
453 old_dev = talloc_strdup(config,
454 val + strlen("mac:"));
457 /* Check for ordered bootdevs */
458 val = get_param(platform, "petitboot,bootdevs");
459 if (!val || !strlen(val)) {
462 pos = talloc_strdup(config, val);
463 end = strchr(pos, '\0');
466 while (pos && pos < end) {
467 opt = talloc(config, struct autoboot_option);
469 if (read_bootdev(config, &pos, opt)) {
470 pb_log("bootdev config is in an unknown format "
471 "(expected uuid:... or mac:...)");
473 if (strchr(pos, delim))
478 new = talloc_realloc(config, new, struct autoboot_option,
486 if (!n_new && !old_dev) {
487 /* If autoboot has been disabled, clear the default options */
488 if (!config->autoboot_enabled) {
489 talloc_free(config->autoboot_opts);
490 config->n_autoboot_opts = 0;
495 conflict = old_dev && (!n_new ||
496 new[0].boot_type == BOOT_DEVICE_TYPE ||
497 /* Canonical UUIDs are 36 characters long */
498 strncmp(new[0].uuid, old_dev, 36));
501 talloc_free(config->autoboot_opts);
502 config->autoboot_opts = new;
503 config->n_autoboot_opts = n_new;
508 * Difference detected, defer to old format in case it has been updated
511 pb_debug("Old autoboot bootdev detected\n");
512 talloc_free(config->autoboot_opts);
513 config->autoboot_opts = talloc(config, struct autoboot_option);
514 config->autoboot_opts[0].boot_type = BOOT_DEVICE_UUID;
515 config->autoboot_opts[0].uuid = talloc_strdup(config, old_dev);
516 config->n_autoboot_opts = 1;
519 static void populate_config(struct platform_powerpc *platform,
520 struct config *config)
524 unsigned long timeout;
526 /* if the "auto-boot?' property is present and "false", disable auto
528 val = get_param(platform, "auto-boot?");
529 config->autoboot_enabled = !val || strcmp(val, "false");
531 val = get_param(platform, "petitboot,timeout");
533 timeout = strtoul(val, &end, 10);
535 if (timeout >= INT_MAX)
537 config->autoboot_timeout_sec = (int)timeout;
541 val = get_param(platform, "petitboot,language");
542 config->lang = val ? talloc_strdup(config, val) : NULL;
544 populate_network_config(platform, config);
546 populate_bootdev_config(platform, config);
548 if (!config->debug) {
549 val = get_param(platform, "petitboot,debug?");
550 config->debug = val && !strcmp(val, "true");
553 val = get_param(platform, "petitboot,write?");
555 config->allow_writes = !strcmp(val, "true");
558 static char *iface_config_str(void *ctx, struct interface_config *config)
562 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
563 * different hardware address formats */
564 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
565 config->hwaddr[0], config->hwaddr[1],
566 config->hwaddr[2], config->hwaddr[3],
567 config->hwaddr[4], config->hwaddr[5]);
569 if (config->ignore) {
570 str = talloc_asprintf_append(str, "ignore");
572 } else if (config->method == CONFIG_METHOD_DHCP) {
573 str = talloc_asprintf_append(str, "dhcp");
575 } else if (config->method == CONFIG_METHOD_STATIC) {
576 str = talloc_asprintf_append(str, "static,%s%s%s",
577 config->static_config.address,
578 config->static_config.gateway ? "," : "",
579 config->static_config.gateway ?: "");
584 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
589 str = talloc_strdup(ctx, "dns,");
590 for (i = 0; i < n; i++) {
591 str = talloc_asprintf_append(str, "%s%s",
599 static void update_string_config(struct platform_powerpc *platform,
600 const char *name, const char *value)
604 cur = get_param(platform, name);
606 /* don't set an empty parameter if it doesn't already exist */
607 if (!cur && !strlen(value))
610 set_param(platform, name, value);
613 static void update_network_config(struct platform_powerpc *platform,
614 struct config *config)
619 val = talloc_strdup(platform, "");
621 for (i = 0; i < config->network.n_interfaces; i++) {
622 char *iface_str = iface_config_str(platform,
623 config->network.interfaces[i]);
624 val = talloc_asprintf_append(val, "%s%s",
625 *val == '\0' ? "" : " ", iface_str);
626 talloc_free(iface_str);
629 if (config->network.n_dns_servers) {
630 char *dns_str = dns_config_str(platform,
631 config->network.dns_servers,
632 config->network.n_dns_servers);
633 val = talloc_asprintf_append(val, "%s%s",
634 *val == '\0' ? "" : " ", dns_str);
635 talloc_free(dns_str);
638 update_string_config(platform, "petitboot,network", val);
643 static void update_bootdev_config(struct platform_powerpc *platform,
644 struct config *config)
646 char *val = NULL, *boot_str = NULL, *tmp = NULL, *first = NULL;
647 struct autoboot_option *opt;
648 const char delim = ' ';
651 if (!config->n_autoboot_opts)
653 else if (config->autoboot_opts[0].boot_type == BOOT_DEVICE_UUID)
654 first = talloc_asprintf(config, "uuid:%s",
655 config->autoboot_opts[0].uuid);
659 for (i = 0; i < config->n_autoboot_opts; i++) {
660 opt = &config->autoboot_opts[i];
661 switch (opt->boot_type) {
662 case BOOT_DEVICE_TYPE:
663 boot_str = talloc_asprintf(config, "%s%c",
664 device_type_name(opt->type),
667 case BOOT_DEVICE_UUID:
668 boot_str = talloc_asprintf(config, "uuid:%s%c",
672 tmp = val = talloc_asprintf_append(val, "%s", boot_str);
675 update_string_config(platform, "petitboot,bootdevs", val);
676 update_string_config(platform, "petitboot,bootdev", first);
679 talloc_free(boot_str);
682 static int update_config(struct platform_powerpc *platform,
683 struct config *config, struct config *defaults)
688 if (config->autoboot_enabled == defaults->autoboot_enabled)
691 val = config->autoboot_enabled ? "true" : "false";
692 update_string_config(platform, "auto-boot?", val);
694 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
697 val = tmp = talloc_asprintf(platform, "%d",
698 config->autoboot_timeout_sec);
700 if (config->ipmi_bootdev == IPMI_BOOTDEV_INVALID &&
701 platform->clear_ipmi_bootdev) {
702 platform->clear_ipmi_bootdev(platform,
703 config->ipmi_bootdev_persistent);
704 config->ipmi_bootdev = IPMI_BOOTDEV_NONE;
705 config->ipmi_bootdev_persistent = false;
708 update_string_config(platform, "petitboot,timeout", val);
712 val = config->lang ?: "";
713 update_string_config(platform, "petitboot,language", val);
715 if (config->allow_writes == defaults->allow_writes)
718 val = config->allow_writes ? "true" : "false";
719 update_string_config(platform, "petitboot,write?", val);
721 update_network_config(platform, config);
723 update_bootdev_config(platform, config);
725 return write_nvram(platform);
728 static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
731 config->ipmi_bootdev = bootdev;
732 config->ipmi_bootdev_persistent = persistent;
735 case IPMI_BOOTDEV_NONE:
736 case IPMI_BOOTDEV_DISK:
737 case IPMI_BOOTDEV_NETWORK:
738 case IPMI_BOOTDEV_CDROM:
741 case IPMI_BOOTDEV_SETUP:
742 config->autoboot_enabled = false;
744 case IPMI_BOOTDEV_SAFE:
745 config->autoboot_enabled = false;
746 config->safe_mode = true;
751 static int read_bootdev_sysparam(const char *name, uint8_t *val)
757 strcpy(path, sysparams_dir);
758 assert(strlen(name) < sizeof(path) - strlen(path));
761 fd = open(path, O_RDONLY);
763 pb_debug("powerpc: can't access sysparam %s\n",
768 rc = read(fd, buf, sizeof(buf));
772 /* bootdev definitions should only be one byte in size */
774 pb_debug("powerpc: sysparam %s read returned %d\n",
779 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
781 if (!ipmi_bootdev_is_valid(buf[0]))
788 static int write_bootdev_sysparam(const char *name, uint8_t val)
793 strcpy(path, sysparams_dir);
794 assert(strlen(name) < sizeof(path) - strlen(path));
797 fd = open(path, O_WRONLY);
799 pb_debug("powerpc: can't access sysparam %s for writing\n",
806 rc = write(fd, &val, sizeof(val));
807 if (rc == sizeof(val)) {
812 if (rc <= 0 && errno != EINTR) {
813 pb_log("powerpc: error updating sysparam %s: %s",
814 name, strerror(errno));
823 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
828 static int clear_ipmi_bootdev_sysparams(
829 struct platform_powerpc *platform __attribute__((unused)),
833 /* invalidate default-boot-device setting */
834 write_bootdev_sysparam("default-boot-device", 0xff);
836 /* invalidate next-boot-device setting */
837 write_bootdev_sysparam("next-boot-device", 0xff);
842 static int get_ipmi_bootdev_sysparams(
843 struct platform_powerpc *platform __attribute__((unused)),
844 uint8_t *bootdev, bool *persistent)
846 uint8_t next_bootdev, default_bootdev;
847 bool next_valid, default_valid;
850 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
851 next_valid = rc == 0;
853 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
854 default_valid = rc == 0;
856 /* nothing valid? no need to change the config */
857 if (!next_valid && !default_valid)
860 *persistent = !next_valid;
861 *bootdev = next_valid ? next_bootdev : default_bootdev;
865 static int clear_ipmi_bootdev_ipmi(struct platform_powerpc *platform,
866 bool persistent __attribute__((unused)))
871 0x05, /* parameter selector: boot flags */
872 0x80, /* data 1: valid */
873 0x00, /* data 2: bootdev: no override */
874 0x00, /* data 3: system defaults */
875 0x00, /* data 4: no request for shared mode, mux defaults */
876 0x00, /* data 5: no instance request */
879 resp_len = sizeof(resp);
881 ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
882 IPMI_CMD_CHASSIS_SET_SYSTEM_BOOT_OPTIONS,
889 static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform,
890 uint8_t *bootdev, bool *persistent)
896 0x05, /* parameter selector: boot flags */
897 0x00, /* no set selector */
898 0x00, /* no block selector */
901 resp_len = sizeof(resp);
902 rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
903 IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS,
908 pb_log("platform: error reading IPMI boot options\n");
912 if (resp_len != sizeof(resp)) {
913 pb_log("platform: unexpected length (%d) in "
914 "boot options response\n", resp_len);
919 pb_log("platform: non-zero completion code %d from IPMI req\n",
924 /* check for correct parameter version */
925 if ((resp[1] & 0xf) != 0x1) {
926 pb_log("platform: unexpected version (0x%x) in "
927 "boot options response\n", resp[0]);
931 /* check for valid paramters */
932 if (resp[2] & 0x80) {
933 pb_debug("platform: boot options are invalid/locked\n");
939 /* check for valid flags */
940 if (!(resp[3] & 0x80)) {
941 pb_debug("platform: boot flags are invalid, ignoring\n");
945 *persistent = resp[3] & 0x40;
946 *bootdev = (resp[4] >> 2) & 0x0f;
950 static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform)
956 0x00, /* sensor number: os boot */
957 0xA9, /* operation: set everything */
958 0x00, /* sensor reading: none */
959 0x40, /* assertion mask lsb: set state 6 */
960 0x00, /* assertion mask msb: none */
961 0x00, /* deassertion mask lsb: none */
962 0x00, /* deassertion mask msb: none */
963 0x00, /* event data 1: none */
964 0x00, /* event data 2: none */
965 0x00, /* event data 3: none */
968 sensor_number = get_ipmi_sensor(platform, IPMI_SENSOR_ID_OS_BOOT);
969 if (sensor_number < 0) {
970 pb_log("Couldn't find OS boot sensor in device tree\n");
974 req[0] = sensor_number;
976 resp_len = sizeof(resp);
978 ipmi_transaction(platform->ipmi, IPMI_NETFN_SE,
982 ipmi_timeout); return 0;
987 static int load_config(struct platform *p, struct config *config)
989 struct platform_powerpc *platform = to_platform_powerpc(p);
992 rc = parse_nvram(platform);
996 populate_config(platform, config);
998 if (platform->get_ipmi_bootdev) {
999 bool bootdev_persistent;
1001 rc = platform->get_ipmi_bootdev(platform, &bootdev,
1002 &bootdev_persistent);
1003 if (!rc && ipmi_bootdev_is_valid(bootdev)) {
1004 set_ipmi_bootdev(config, bootdev, bootdev_persistent);
1011 static int save_config(struct platform *p, struct config *config)
1013 struct platform_powerpc *platform = to_platform_powerpc(p);
1014 struct config *defaults;
1017 defaults = talloc_zero(platform, struct config);
1018 config_set_defaults(defaults);
1020 rc = update_config(platform, config, defaults);
1022 talloc_free(defaults);
1026 static void pre_boot(struct platform *p, const struct config *config)
1028 struct platform_powerpc *platform = to_platform_powerpc(p);
1030 if (!config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev)
1031 platform->clear_ipmi_bootdev(platform, false);
1033 if (platform->set_os_boot_sensor)
1034 platform->set_os_boot_sensor(platform);
1037 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
1039 struct platform_powerpc *platform = p->platform_data;
1040 char *buf, *filename;
1043 filename = talloc_asprintf(platform, "%smodel", devtree_dir);
1044 rc = read_file(platform, filename, &buf, &len);
1046 sysinfo->type = talloc_steal(sysinfo, buf);
1047 talloc_free(filename);
1049 filename = talloc_asprintf(platform, "%ssystem-id", devtree_dir);
1050 rc = read_file(platform, filename, &buf, &len);
1052 sysinfo->identifier = talloc_steal(sysinfo, buf);
1053 talloc_free(filename);
1058 static bool probe(struct platform *p, void *ctx)
1060 struct platform_powerpc *platform;
1061 struct stat statbuf;
1064 /* we need a device tree */
1065 rc = stat("/proc/device-tree", &statbuf);
1069 if (!S_ISDIR(statbuf.st_mode))
1072 platform = talloc_zero(ctx, struct platform_powerpc);
1073 list_init(&platform->params);
1075 p->platform_data = platform;
1077 if (ipmi_present()) {
1078 pb_debug("platform: using direct IPMI for IPMI paramters\n");
1079 platform->ipmi = ipmi_open(platform);
1080 platform->get_ipmi_bootdev = get_ipmi_bootdev_ipmi;
1081 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_ipmi;
1082 platform->set_os_boot_sensor = set_ipmi_os_boot_sensor;
1084 } else if (!stat(sysparams_dir, &statbuf)) {
1085 pb_debug("platform: using sysparams for IPMI paramters\n");
1086 platform->get_ipmi_bootdev = get_ipmi_bootdev_sysparams;
1087 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_sysparams;
1090 pb_log("platform: no IPMI parameter support\n");
1097 static struct platform platform_powerpc = {
1099 .dhcp_arch_id = 0x000e,
1101 .load_config = load_config,
1102 .save_config = save_config,
1103 .pre_boot = pre_boot,
1104 .get_sysinfo = get_sysinfo,
1107 register_platform(platform_powerpc);