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);
43 int (*set_os_boot_sensor)(
44 struct platform_powerpc *platform);
47 static const char *known_params[] = {
58 #define to_platform_powerpc(p) \
59 (struct platform_powerpc *)(p->platform_data)
61 /* a partition max a max size of 64k * 16bytes = 1M */
62 static const int max_partition_size = 64 * 1024 * 16;
64 static bool param_is_known(const char *param, unsigned int len)
66 const char *known_param;
69 for (i = 0; known_params[i]; i++) {
70 known_param = known_params[i];
71 if (len == strlen(known_param) &&
72 !strncmp(param, known_param, len))
79 static int parse_nvram_params(struct platform_powerpc *platform,
82 char *pos, *name, *value;
83 unsigned int paramlen;
86 /* discard 2 header lines:
93 for (i = 0; i < len; i++) {
101 fprintf(stderr, "failure parsing nvram output\n");
105 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
106 unsigned int namelen;
110 newline = strchr(pos, '\n');
116 paramlen = strlen(pos);
119 value = strchr(pos, '=');
123 namelen = value - name;
127 if (!param_is_known(name, namelen))
132 param = talloc(platform, struct param);
133 param->modified = false;
134 param->name = talloc_strndup(platform, name, namelen);
135 param->value = talloc_strdup(platform, value);
136 list_add(&platform->params, ¶m->list);
142 static int parse_nvram(struct platform_powerpc *platform)
144 struct process *process;
149 argv[1] = "--print-config";
150 argv[2] = "--partition";
154 process = process_create(platform);
155 process->path = "nvram";
156 process->argv = argv;
157 process->keep_stdout = true;
159 rc = process_run_sync(process);
161 if (rc || !process_exit_ok(process)) {
162 fprintf(stderr, "nvram process returned "
163 "non-zero exit status\n");
166 rc = parse_nvram_params(platform, process->stdout_buf,
167 process->stdout_len);
170 process_release(process);
174 static int write_nvram(struct platform_powerpc *platform)
176 struct process *process;
182 argv[1] = "--update-config";
184 argv[3] = "--partition";
188 process = process_create(platform);
189 process->path = "nvram";
190 process->argv = argv;
192 list_for_each_entry(&platform->params, param, list) {
195 if (!param->modified)
198 paramstr = talloc_asprintf(platform, "%s=%s",
199 param->name, param->value);
202 rc = process_run_sync(process);
204 talloc_free(paramstr);
206 if (rc || !process_exit_ok(process)) {
208 pb_log("nvram update process returned "
209 "non-zero exit status\n");
214 process_release(process);
218 static const char *get_param(struct platform_powerpc *platform,
223 list_for_each_entry(&platform->params, param, list)
224 if (!strcmp(param->name, name))
229 static void set_param(struct platform_powerpc *platform, const char *name,
234 list_for_each_entry(&platform->params, param, list) {
235 if (strcmp(param->name, name))
238 if (!strcmp(param->value, value))
241 talloc_free(param->value);
242 param->value = talloc_strdup(param, value);
243 param->modified = true;
248 param = talloc(platform, struct param);
249 param->modified = true;
250 param->name = talloc_strdup(platform, name);
251 param->value = talloc_strdup(platform, value);
252 list_add(&platform->params, ¶m->list);
255 static int parse_hwaddr(struct interface_config *ifconf, char *str)
259 if (strlen(str) != strlen("00:00:00:00:00:00"))
262 for (i = 0; i < HWADDR_SIZE; i++) {
266 byte[0] = str[i * 3 + 0];
267 byte[1] = str[i * 3 + 1];
270 x = strtoul(byte, &endp, 16);
271 if (endp != byte + 2)
274 ifconf->hwaddr[i] = x & 0xff;
280 static int parse_one_interface_config(struct config *config,
283 struct interface_config *ifconf;
286 ifconf = talloc_zero(config, struct interface_config);
288 if (!confstr || !strlen(confstr))
291 /* first token should be the mac address */
292 tok = strtok_r(confstr, ",", &saveptr);
296 if (parse_hwaddr(ifconf, tok))
299 /* second token is the method */
300 tok = strtok_r(NULL, ",", &saveptr);
301 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
302 ifconf->ignore = true;
304 } else if (!strcmp(tok, "dhcp")) {
305 ifconf->method = CONFIG_METHOD_DHCP;
307 } else if (!strcmp(tok, "static")) {
308 ifconf->method = CONFIG_METHOD_STATIC;
310 /* ip/mask, [optional] gateway */
311 tok = strtok_r(NULL, ",", &saveptr);
314 ifconf->static_config.address =
315 talloc_strdup(ifconf, tok);
317 tok = strtok_r(NULL, ",", &saveptr);
319 ifconf->static_config.gateway =
320 talloc_strdup(ifconf, tok);
324 pb_log("Unknown network configuration method %s\n", tok);
328 config->network.interfaces = talloc_realloc(config,
329 config->network.interfaces,
330 struct interface_config *,
331 ++config->network.n_interfaces);
333 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
341 static int parse_one_dns_config(struct config *config,
344 char *tok, *saveptr = NULL;
346 for (tok = strtok_r(confstr, ",", &saveptr); tok;
347 tok = strtok_r(NULL, ",", &saveptr)) {
349 char *server = talloc_strdup(config, tok);
351 config->network.dns_servers = talloc_realloc(config,
352 config->network.dns_servers, const char *,
353 ++config->network.n_dns_servers);
355 config->network.dns_servers[config->network.n_dns_servers - 1]
362 static void populate_network_config(struct platform_powerpc *platform,
363 struct config *config)
365 char *val, *saveptr = NULL;
369 cval = get_param(platform, "petitboot,network");
370 if (!cval || !strlen(cval))
373 val = talloc_strdup(config, cval);
378 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
382 if (!strncasecmp(tok, "dns,", strlen("dns,")))
383 parse_one_dns_config(config, tok + strlen("dns,"));
385 parse_one_interface_config(config, tok);
392 static int read_bootdev(void *ctx, char **pos, struct autoboot_option *opt)
394 char *delim = strchr(*pos, ' ');
395 int len, prefix = 0, rc = -1;
396 enum device_type type;
398 if (!strncmp(*pos, "uuid:", strlen("uuid:"))) {
399 prefix = strlen("uuid:");
400 opt->boot_type = BOOT_DEVICE_UUID;
402 } else if (!strncmp(*pos, "mac:", strlen("mac:"))) {
403 prefix = strlen("mac:");
404 opt->boot_type = BOOT_DEVICE_UUID;
407 type = find_device_type(*pos);
408 if (type != DEVICE_TYPE_UNKNOWN) {
410 opt->boot_type = BOOT_DEVICE_TYPE;
415 if (opt->boot_type == BOOT_DEVICE_UUID) {
417 len = (int)(delim - *pos) - prefix;
421 opt->uuid = talloc_strndup(ctx, *pos + prefix, len);
424 /* Always advance pointer to next option or end */
428 *pos += strlen(*pos);
433 static void populate_bootdev_config(struct platform_powerpc *platform,
434 struct config *config)
436 struct autoboot_option *opt, *new = NULL;
437 char *pos, *end, *old_dev = NULL;
438 const char delim = ' ';
439 unsigned int n_new = 0;
443 /* Check for old-style bootdev */
444 val = get_param(platform, "petitboot,bootdev");
445 if (val && strlen(val)) {
446 pos = talloc_strdup(config, val);
447 if (!strncmp(val, "uuid:", strlen("uuid:")))
448 old_dev = talloc_strdup(config,
449 val + strlen("uuid:"));
450 else if (!strncmp(val, "mac:", strlen("mac:")))
451 old_dev = talloc_strdup(config,
452 val + strlen("mac:"));
455 /* Check for ordered bootdevs */
456 val = get_param(platform, "petitboot,bootdevs");
457 if (!val || !strlen(val)) {
460 pos = talloc_strdup(config, val);
461 end = strchr(pos, '\0');
464 while (pos && pos < end) {
465 opt = talloc(config, struct autoboot_option);
467 if (read_bootdev(config, &pos, opt)) {
468 pb_log("bootdev config is in an unknown format "
469 "(expected uuid:... or mac:...)");
471 if (strchr(pos, delim))
476 new = talloc_realloc(config, new, struct autoboot_option,
484 if (!n_new && !old_dev) {
485 /* If autoboot has been disabled, clear the default options */
486 if (!config->autoboot_enabled) {
487 talloc_free(config->autoboot_opts);
488 config->n_autoboot_opts = 0;
493 conflict = old_dev && (!n_new ||
494 new[0].boot_type == BOOT_DEVICE_TYPE ||
495 /* Canonical UUIDs are 36 characters long */
496 strncmp(new[0].uuid, old_dev, 36));
499 talloc_free(config->autoboot_opts);
500 config->autoboot_opts = new;
501 config->n_autoboot_opts = n_new;
506 * Difference detected, defer to old format in case it has been updated
509 pb_debug("Old autoboot bootdev detected\n");
510 talloc_free(config->autoboot_opts);
511 config->autoboot_opts = talloc(config, struct autoboot_option);
512 config->autoboot_opts[0].boot_type = BOOT_DEVICE_UUID;
513 config->autoboot_opts[0].uuid = talloc_strdup(config, old_dev);
514 config->n_autoboot_opts = 1;
517 static void populate_config(struct platform_powerpc *platform,
518 struct config *config)
522 unsigned long timeout;
524 /* if the "auto-boot?' property is present and "false", disable auto
526 val = get_param(platform, "auto-boot?");
527 config->autoboot_enabled = !val || strcmp(val, "false");
529 val = get_param(platform, "petitboot,timeout");
531 timeout = strtoul(val, &end, 10);
533 if (timeout >= INT_MAX)
535 config->autoboot_timeout_sec = (int)timeout;
539 val = get_param(platform, "petitboot,language");
540 config->lang = val ? talloc_strdup(config, val) : NULL;
542 populate_network_config(platform, config);
544 populate_bootdev_config(platform, config);
546 if (!config->debug) {
547 val = get_param(platform, "petitboot,debug?");
548 config->debug = val && !strcmp(val, "true");
552 static char *iface_config_str(void *ctx, struct interface_config *config)
556 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
557 * different hardware address formats */
558 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
559 config->hwaddr[0], config->hwaddr[1],
560 config->hwaddr[2], config->hwaddr[3],
561 config->hwaddr[4], config->hwaddr[5]);
563 if (config->ignore) {
564 str = talloc_asprintf_append(str, "ignore");
566 } else if (config->method == CONFIG_METHOD_DHCP) {
567 str = talloc_asprintf_append(str, "dhcp");
569 } else if (config->method == CONFIG_METHOD_STATIC) {
570 str = talloc_asprintf_append(str, "static,%s%s%s",
571 config->static_config.address,
572 config->static_config.gateway ? "," : "",
573 config->static_config.gateway ?: "");
578 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
583 str = talloc_strdup(ctx, "dns,");
584 for (i = 0; i < n; i++) {
585 str = talloc_asprintf_append(str, "%s%s",
593 static void update_string_config(struct platform_powerpc *platform,
594 const char *name, const char *value)
598 cur = get_param(platform, name);
600 /* don't set an empty parameter if it doesn't already exist */
601 if (!cur && !strlen(value))
604 set_param(platform, name, value);
607 static void update_network_config(struct platform_powerpc *platform,
608 struct config *config)
613 val = talloc_strdup(platform, "");
615 for (i = 0; i < config->network.n_interfaces; i++) {
616 char *iface_str = iface_config_str(platform,
617 config->network.interfaces[i]);
618 val = talloc_asprintf_append(val, "%s%s",
619 *val == '\0' ? "" : " ", iface_str);
620 talloc_free(iface_str);
623 if (config->network.n_dns_servers) {
624 char *dns_str = dns_config_str(platform,
625 config->network.dns_servers,
626 config->network.n_dns_servers);
627 val = talloc_asprintf_append(val, "%s%s",
628 *val == '\0' ? "" : " ", dns_str);
629 talloc_free(dns_str);
632 update_string_config(platform, "petitboot,network", val);
637 static void update_bootdev_config(struct platform_powerpc *platform,
638 struct config *config)
640 char *val = NULL, *boot_str = NULL, *tmp = NULL, *first = NULL;
641 struct autoboot_option *opt;
642 const char delim = ' ';
645 if (!config->n_autoboot_opts)
647 else if (config->autoboot_opts[0].boot_type == BOOT_DEVICE_UUID)
648 first = talloc_asprintf(config, "uuid:%s",
649 config->autoboot_opts[0].uuid);
653 for (i = 0; i < config->n_autoboot_opts; i++) {
654 opt = &config->autoboot_opts[i];
655 switch (opt->boot_type) {
656 case BOOT_DEVICE_TYPE:
657 boot_str = talloc_asprintf(config, "%s%c",
658 device_type_name(opt->type),
661 case BOOT_DEVICE_UUID:
662 boot_str = talloc_asprintf(config, "uuid:%s%c",
666 tmp = val = talloc_asprintf_append(val, boot_str);
669 update_string_config(platform, "petitboot,bootdevs", val);
670 update_string_config(platform, "petitboot,bootdev", first);
673 talloc_free(boot_str);
676 static int update_config(struct platform_powerpc *platform,
677 struct config *config, struct config *defaults)
682 if (config->autoboot_enabled == defaults->autoboot_enabled)
685 val = config->autoboot_enabled ? "true" : "false";
686 update_string_config(platform, "auto-boot?", val);
688 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
691 val = tmp = talloc_asprintf(platform, "%d",
692 config->autoboot_timeout_sec);
694 update_string_config(platform, "petitboot,timeout", val);
698 val = config->lang ?: "";
699 update_string_config(platform, "petitboot,language", val);
701 update_network_config(platform, config);
703 update_bootdev_config(platform, config);
705 return write_nvram(platform);
708 static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
711 config->ipmi_bootdev = bootdev;
712 config->ipmi_bootdev_persistent = persistent;
715 case IPMI_BOOTDEV_NONE:
716 case IPMI_BOOTDEV_DISK:
717 case IPMI_BOOTDEV_NETWORK:
718 case IPMI_BOOTDEV_CDROM:
720 case IPMI_BOOTDEV_SETUP:
721 config->autoboot_enabled = false;
723 case IPMI_BOOTDEV_SAFE:
724 config->autoboot_enabled = false;
725 config->safe_mode = true;
730 static int read_bootdev_sysparam(const char *name, uint8_t *val)
736 strcpy(path, sysparams_dir);
737 assert(strlen(name) < sizeof(path) - strlen(path));
740 fd = open(path, O_RDONLY);
742 pb_debug("powerpc: can't access sysparam %s\n",
747 rc = read(fd, buf, sizeof(buf));
751 /* bootdev definitions should only be one byte in size */
753 pb_debug("powerpc: sysparam %s read returned %d\n",
758 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
760 if (!ipmi_bootdev_is_valid(buf[0]))
767 static int write_bootdev_sysparam(const char *name, uint8_t val)
772 strcpy(path, sysparams_dir);
773 assert(strlen(name) < sizeof(path) - strlen(path));
776 fd = open(path, O_WRONLY);
778 pb_debug("powerpc: can't access sysparam %s for writing\n",
785 rc = write(fd, &val, sizeof(val));
786 if (rc == sizeof(val)) {
791 if (rc <= 0 && errno != EINTR) {
792 pb_log("powerpc: error updating sysparam %s: %s",
793 name, strerror(errno));
802 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
807 static int clear_ipmi_bootdev_sysparams(
808 struct platform_powerpc *platform __attribute__((unused)))
810 /* invalidate next-boot-device setting */
811 write_bootdev_sysparam("next-boot-device", 0xff);
815 static int get_ipmi_bootdev_sysparams(
816 struct platform_powerpc *platform __attribute__((unused)),
817 uint8_t *bootdev, bool *persistent)
819 uint8_t next_bootdev, default_bootdev;
820 bool next_valid, default_valid;
823 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
824 next_valid = rc == 0;
826 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
827 default_valid = rc == 0;
829 /* nothing valid? no need to change the config */
830 if (!next_valid && !default_valid)
833 *persistent = !next_valid;
834 *bootdev = next_valid ? next_bootdev : default_bootdev;
838 static int clear_ipmi_bootdev_ipmi(struct platform_powerpc *platform)
843 0x05, /* parameter selector: boot flags */
844 0x80, /* data 1: valid */
845 0x00, /* data 2: bootdev: no override */
846 0x00, /* data 3: system defaults */
847 0x00, /* data 4: no request for shared mode, mux defaults */
848 0x00, /* data 5: no instance request */
851 resp_len = sizeof(resp);
853 ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
854 IPMI_CMD_CHASSIS_SET_SYSTEM_BOOT_OPTIONS,
861 static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform,
862 uint8_t *bootdev, bool *persistent)
868 0x05, /* parameter selector: boot flags */
869 0x00, /* no set selector */
870 0x00, /* no block selector */
873 resp_len = sizeof(resp);
874 rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
875 IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS,
880 pb_log("platform: error reading IPMI boot options\n");
884 if (resp_len != sizeof(resp)) {
885 pb_log("platform: unexpected length (%d) in "
886 "boot options response\n", resp_len);
891 pb_log("platform: non-zero completion code %d from IPMI req\n",
896 /* check for correct parameter version */
897 if ((resp[1] & 0xf) != 0x1) {
898 pb_log("platform: unexpected version (0x%x) in "
899 "boot options response\n", resp[0]);
903 /* check for valid paramters */
904 if (resp[2] & 0x80) {
905 pb_debug("platform: boot options are invalid/locked\n");
911 /* check for valid flags */
912 if (!(resp[3] & 0x80)) {
913 pb_debug("platform: boot flags are invalid, ignoring\n");
917 *persistent = resp[3] & 0x40;
918 *bootdev = (resp[4] >> 2) & 0x0f;
922 static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform)
928 0x00, /* sensor number: os boot */
929 0xA9, /* operation: set everything */
930 0x00, /* sensor reading: none */
931 0x40, /* assertion mask lsb: set state 6 */
932 0x00, /* assertion mask msb: none */
933 0x00, /* deassertion mask lsb: none */
934 0x00, /* deassertion mask msb: none */
935 0x00, /* event data 1: none */
936 0x00, /* event data 2: none */
937 0x00, /* event data 3: none */
940 sensor_number = get_ipmi_sensor(platform, IPMI_SENSOR_ID_OS_BOOT);
941 if (sensor_number < 0) {
942 pb_log("Couldn't find OS boot sensor in device tree\n");
946 req[0] = sensor_number;
948 resp_len = sizeof(resp);
950 ipmi_transaction(platform->ipmi, IPMI_NETFN_SE,
954 ipmi_timeout); return 0;
959 static int load_config(struct platform *p, struct config *config)
961 struct platform_powerpc *platform = to_platform_powerpc(p);
964 rc = parse_nvram(platform);
968 populate_config(platform, config);
970 if (platform->get_ipmi_bootdev) {
971 bool bootdev_persistent;
973 rc = platform->get_ipmi_bootdev(platform, &bootdev,
974 &bootdev_persistent);
975 if (!rc && ipmi_bootdev_is_valid(bootdev)) {
976 set_ipmi_bootdev(config, bootdev, bootdev_persistent);
983 static int save_config(struct platform *p, struct config *config)
985 struct platform_powerpc *platform = to_platform_powerpc(p);
986 struct config *defaults;
989 defaults = talloc_zero(platform, struct config);
990 config_set_defaults(defaults);
992 rc = update_config(platform, config, defaults);
994 talloc_free(defaults);
998 static void pre_boot(struct platform *p, const struct config *config)
1000 struct platform_powerpc *platform = to_platform_powerpc(p);
1002 if (!config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev)
1003 platform->clear_ipmi_bootdev(platform);
1005 if (platform->set_os_boot_sensor)
1006 platform->set_os_boot_sensor(platform);
1009 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
1011 struct platform_powerpc *platform = p->platform_data;
1012 char *buf, *filename;
1015 filename = talloc_asprintf(platform, "%smodel", devtree_dir);
1016 rc = read_file(platform, filename, &buf, &len);
1018 sysinfo->type = talloc_steal(sysinfo, buf);
1019 talloc_free(filename);
1021 filename = talloc_asprintf(platform, "%ssystem-id", devtree_dir);
1022 rc = read_file(platform, filename, &buf, &len);
1024 sysinfo->identifier = talloc_steal(sysinfo, buf);
1025 talloc_free(filename);
1030 static bool probe(struct platform *p, void *ctx)
1032 struct platform_powerpc *platform;
1033 struct stat statbuf;
1036 /* we need a device tree */
1037 rc = stat("/proc/device-tree", &statbuf);
1041 if (!S_ISDIR(statbuf.st_mode))
1044 platform = talloc_zero(ctx, struct platform_powerpc);
1045 list_init(&platform->params);
1047 p->platform_data = platform;
1049 if (ipmi_present()) {
1050 pb_debug("platform: using direct IPMI for IPMI paramters\n");
1051 platform->ipmi = ipmi_open(platform);
1052 platform->get_ipmi_bootdev = get_ipmi_bootdev_ipmi;
1053 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_ipmi;
1054 platform->set_os_boot_sensor = set_ipmi_os_boot_sensor;
1056 } else if (!stat(sysparams_dir, &statbuf)) {
1057 pb_debug("platform: using sysparams for IPMI paramters\n");
1058 platform->get_ipmi_bootdev = get_ipmi_bootdev_sysparams;
1059 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_sysparams;
1062 pb_log("platform: no IPMI parameter support\n");
1069 static struct platform platform_powerpc = {
1071 .dhcp_arch_id = 0x000e,
1073 .load_config = load_config,
1074 .save_config = save_config,
1075 .pre_boot = pre_boot,
1076 .get_sysinfo = get_sysinfo,
1079 register_platform(platform_powerpc);