12 #include <file/file.h>
13 #include <talloc/talloc.h>
14 #include <list/list.h>
16 #include <process/process.h>
21 static const char *partition = "common";
22 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
23 static const char *devtree_dir = "/proc/device-tree/";
29 struct list_item list;
32 struct platform_powerpc {
34 int (*get_ipmi_bootdev)(
35 struct platform_powerpc *platform,
36 uint8_t *bootdev, bool *persistent);
37 int (*clear_ipmi_bootdev)(
38 struct platform_powerpc *platform);
41 static const char *known_params[] = {
51 #define to_platform_powerpc(p) \
52 (struct platform_powerpc *)(p->platform_data)
54 /* a partition max a max size of 64k * 16bytes = 1M */
55 static const int max_partition_size = 64 * 1024 * 16;
57 static bool param_is_known(const char *param, unsigned int len)
59 const char *known_param;
62 for (i = 0; known_params[i]; i++) {
63 known_param = known_params[i];
64 if (len == strlen(known_param) &&
65 !strncmp(param, known_param, len))
72 static int parse_nvram_params(struct platform_powerpc *platform,
75 char *pos, *name, *value;
76 unsigned int paramlen;
79 /* discard 2 header lines:
86 for (i = 0; i < len; i++) {
94 fprintf(stderr, "failure parsing nvram output\n");
98 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
103 newline = strchr(pos, '\n');
109 paramlen = strlen(pos);
112 value = strchr(pos, '=');
116 namelen = value - name;
120 if (!param_is_known(name, namelen))
125 param = talloc(platform, struct param);
126 param->modified = false;
127 param->name = talloc_strndup(platform, name, namelen);
128 param->value = talloc_strdup(platform, value);
129 list_add(&platform->params, ¶m->list);
135 static int parse_nvram(struct platform_powerpc *platform)
137 struct process *process;
142 argv[1] = "--print-config";
143 argv[2] = "--partition";
147 process = process_create(platform);
148 process->path = "nvram";
149 process->argv = argv;
150 process->keep_stdout = true;
152 rc = process_run_sync(process);
154 if (rc || !process_exit_ok(process)) {
155 fprintf(stderr, "nvram process returned "
156 "non-zero exit status\n");
159 rc = parse_nvram_params(platform, process->stdout_buf,
160 process->stdout_len);
163 process_release(process);
167 static int write_nvram(struct platform_powerpc *platform)
169 struct process *process;
175 argv[1] = "--update-config";
177 argv[3] = "--partition";
181 process = process_create(platform);
182 process->path = "nvram";
183 process->argv = argv;
185 list_for_each_entry(&platform->params, param, list) {
188 if (!param->modified)
191 paramstr = talloc_asprintf(platform, "%s=%s",
192 param->name, param->value);
195 rc = process_run_sync(process);
197 talloc_free(paramstr);
199 if (rc || !process_exit_ok(process)) {
201 pb_log("nvram update process returned "
202 "non-zero exit status\n");
207 process_release(process);
211 static const char *get_param(struct platform_powerpc *platform,
216 list_for_each_entry(&platform->params, param, list)
217 if (!strcmp(param->name, name))
222 static void set_param(struct platform_powerpc *platform, const char *name,
227 list_for_each_entry(&platform->params, param, list) {
228 if (strcmp(param->name, name))
231 if (!strcmp(param->value, value))
234 talloc_free(param->value);
235 param->value = talloc_strdup(param, value);
236 param->modified = true;
241 param = talloc(platform, struct param);
242 param->modified = true;
243 param->name = talloc_strdup(platform, name);
244 param->value = talloc_strdup(platform, value);
245 list_add(&platform->params, ¶m->list);
248 static int parse_hwaddr(struct interface_config *ifconf, char *str)
252 if (strlen(str) != strlen("00:00:00:00:00:00"))
255 for (i = 0; i < HWADDR_SIZE; i++) {
259 byte[0] = str[i * 3 + 0];
260 byte[1] = str[i * 3 + 1];
263 x = strtoul(byte, &endp, 16);
264 if (endp != byte + 2)
267 ifconf->hwaddr[i] = x & 0xff;
273 static int parse_one_interface_config(struct config *config,
276 struct interface_config *ifconf;
279 ifconf = talloc_zero(config, struct interface_config);
281 if (!confstr || !strlen(confstr))
284 /* first token should be the mac address */
285 tok = strtok_r(confstr, ",", &saveptr);
289 if (parse_hwaddr(ifconf, tok))
292 /* second token is the method */
293 tok = strtok_r(NULL, ",", &saveptr);
294 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
295 ifconf->ignore = true;
297 } else if (!strcmp(tok, "dhcp")) {
298 ifconf->method = CONFIG_METHOD_DHCP;
300 } else if (!strcmp(tok, "static")) {
301 ifconf->method = CONFIG_METHOD_STATIC;
303 /* ip/mask, [optional] gateway */
304 tok = strtok_r(NULL, ",", &saveptr);
307 ifconf->static_config.address =
308 talloc_strdup(ifconf, tok);
310 tok = strtok_r(NULL, ",", &saveptr);
312 ifconf->static_config.gateway =
313 talloc_strdup(ifconf, tok);
317 pb_log("Unknown network configuration method %s\n", tok);
321 config->network.interfaces = talloc_realloc(config,
322 config->network.interfaces,
323 struct interface_config *,
324 ++config->network.n_interfaces);
326 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
334 static int parse_one_dns_config(struct config *config,
339 for (tok = strtok_r(confstr, ",", &saveptr); tok;
340 tok = strtok_r(NULL, ",", &saveptr)) {
342 char *server = talloc_strdup(config, tok);
344 config->network.dns_servers = talloc_realloc(config,
345 config->network.dns_servers, const char *,
346 ++config->network.n_dns_servers);
348 config->network.dns_servers[config->network.n_dns_servers - 1]
355 static void populate_network_config(struct platform_powerpc *platform,
356 struct config *config)
358 char *val, *saveptr = NULL;
362 cval = get_param(platform, "petitboot,network");
363 if (!cval || !strlen(cval))
366 val = talloc_strdup(config, cval);
371 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
375 if (!strncasecmp(tok, "dns,", strlen("dns,")))
376 parse_one_dns_config(config, tok + strlen("dns,"));
378 parse_one_interface_config(config, tok);
385 static void populate_bootdev_config(struct platform_powerpc *platform,
386 struct config *config)
391 config->boot_device = NULL;
393 val = get_param(platform, "petitboot,bootdev");
394 if (!val || !strlen(val))
397 if (!strncmp(val, "uuid:", strlen("uuid:"))) {
398 config->boot_device = talloc_strdup(config,
399 val + strlen("uuid:"));
401 } else if (!strncmp(val, "mac:", strlen("mac:"))) {
402 config->boot_device = talloc_strdup(config,
403 val + strlen("mac:"));
406 pb_log("bootdev config is in an unknown format "
407 "(expected uuid:... or mac:...)");
411 static void populate_config(struct platform_powerpc *platform,
412 struct config *config)
416 unsigned long timeout;
418 /* if the "auto-boot?' property is present and "false", disable auto
420 val = get_param(platform, "auto-boot?");
421 config->autoboot_enabled = !val || strcmp(val, "false");
423 val = get_param(platform, "petitboot,timeout");
425 timeout = strtoul(val, &end, 10);
427 if (timeout >= INT_MAX)
429 config->autoboot_timeout_sec = (int)timeout;
433 val = get_param(platform, "petitboot,language");
434 config->lang = val ? talloc_strdup(config, val) : NULL;
436 populate_network_config(platform, config);
438 populate_bootdev_config(platform, config);
440 if (!config->debug) {
441 val = get_param(platform, "petitboot,debug?");
442 config->debug = val && !strcmp(val, "true");
446 static char *iface_config_str(void *ctx, struct interface_config *config)
450 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
451 * different hardware address formats */
452 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
453 config->hwaddr[0], config->hwaddr[1],
454 config->hwaddr[2], config->hwaddr[3],
455 config->hwaddr[4], config->hwaddr[5]);
457 if (config->ignore) {
458 str = talloc_asprintf_append(str, "ignore");
460 } else if (config->method == CONFIG_METHOD_DHCP) {
461 str = talloc_asprintf_append(str, "dhcp");
463 } else if (config->method == CONFIG_METHOD_STATIC) {
464 str = talloc_asprintf_append(str, "static,%s%s%s",
465 config->static_config.address,
466 config->static_config.gateway ? "," : "",
467 config->static_config.gateway ?: "");
472 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
477 str = talloc_strdup(ctx, "dns,");
478 for (i = 0; i < n; i++) {
479 str = talloc_asprintf_append(str, "%s%s",
487 static void update_string_config(struct platform_powerpc *platform,
488 const char *name, const char *value)
492 cur = get_param(platform, name);
494 /* don't set an empty parameter if it doesn't already exist */
495 if (!cur && !strlen(value))
498 set_param(platform, name, value);
501 static void update_network_config(struct platform_powerpc *platform,
502 struct config *config)
507 val = talloc_strdup(platform, "");
509 for (i = 0; i < config->network.n_interfaces; i++) {
510 char *iface_str = iface_config_str(platform,
511 config->network.interfaces[i]);
512 val = talloc_asprintf_append(val, "%s%s",
513 *val == '\0' ? "" : " ", iface_str);
514 talloc_free(iface_str);
517 if (config->network.n_dns_servers) {
518 char *dns_str = dns_config_str(platform,
519 config->network.dns_servers,
520 config->network.n_dns_servers);
521 val = talloc_asprintf_append(val, "%s%s",
522 *val == '\0' ? "" : " ", dns_str);
523 talloc_free(dns_str);
526 update_string_config(platform, "petitboot,network", val);
531 static void update_bootdev_config(struct platform_powerpc *platform,
532 struct config *config)
534 char *val, *tmp = NULL;
536 if (!config->boot_device)
539 tmp = val = talloc_asprintf(platform,
540 "uuid:%s", config->boot_device);
542 update_string_config(platform, "petitboot,bootdev", val);
546 static int update_config(struct platform_powerpc *platform,
547 struct config *config, struct config *defaults)
552 if (config->autoboot_enabled == defaults->autoboot_enabled)
555 val = config->autoboot_enabled ? "true" : "false";
556 update_string_config(platform, "auto-boot?", val);
558 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
561 val = tmp = talloc_asprintf(platform, "%d",
562 config->autoboot_timeout_sec);
564 update_string_config(platform, "petitboot,timeout", val);
568 val = config->lang ?: "";
569 update_string_config(platform, "petitboot,language", val);
571 update_network_config(platform, config);
573 update_bootdev_config(platform, config);
575 return write_nvram(platform);
578 static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
581 config->ipmi_bootdev = bootdev;
582 config->ipmi_bootdev_persistent = persistent;
585 case IPMI_BOOTDEV_NONE:
586 case IPMI_BOOTDEV_DISK:
587 case IPMI_BOOTDEV_NETWORK:
588 case IPMI_BOOTDEV_CDROM:
590 case IPMI_BOOTDEV_SETUP:
591 config->autoboot_enabled = false;
593 case IPMI_BOOTDEV_SAFE:
594 config->autoboot_enabled = false;
595 config->safe_mode = true;
600 static int read_bootdev_sysparam(const char *name, uint8_t *val)
606 strcpy(path, sysparams_dir);
607 assert(strlen(name) < sizeof(path) - strlen(path));
610 fd = open(path, O_RDONLY);
612 pb_debug("powerpc: can't access sysparam %s\n",
617 rc = read(fd, buf, sizeof(buf));
621 /* bootdev definitions should only be one byte in size */
623 pb_debug("powerpc: sysparam %s read returned %d\n",
628 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
630 if (!ipmi_bootdev_is_valid(buf[0]))
637 static int write_bootdev_sysparam(const char *name, uint8_t val)
642 strcpy(path, sysparams_dir);
643 assert(strlen(name) < sizeof(path) - strlen(path));
646 fd = open(path, O_WRONLY);
648 pb_debug("powerpc: can't access sysparam %s for writing\n",
655 rc = write(fd, &val, sizeof(val));
656 if (rc == sizeof(val)) {
661 if (rc <= 0 && errno != EINTR) {
662 pb_log("powerpc: error updating sysparam %s: %s",
663 name, strerror(errno));
672 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
677 static int clear_ipmi_bootdev_sysparams(
678 struct platform_powerpc *platform __attribute__((unused)))
680 /* invalidate next-boot-device setting */
681 write_bootdev_sysparam("next-boot-device", 0xff);
685 static int get_ipmi_bootdev_sysparams(
686 struct platform_powerpc *platform __attribute__((unused)),
687 uint8_t *bootdev, bool *persistent)
689 uint8_t next_bootdev, default_bootdev;
690 bool next_valid, default_valid;
693 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
694 next_valid = rc == 0;
696 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
697 default_valid = rc == 0;
699 /* nothing valid? no need to change the config */
700 if (!next_valid && !default_valid)
703 *persistent = !next_valid;
704 *bootdev = next_valid ? next_bootdev : default_bootdev;
708 static int load_config(struct platform *p, struct config *config)
710 struct platform_powerpc *platform = to_platform_powerpc(p);
713 rc = parse_nvram(platform);
717 populate_config(platform, config);
719 if (platform->get_ipmi_bootdev) {
720 bool bootdev_persistent;
722 rc = platform->get_ipmi_bootdev(platform, &bootdev,
723 &bootdev_persistent);
724 if (!rc && ipmi_bootdev_is_valid(bootdev)) {
725 set_ipmi_bootdev(config, bootdev, bootdev_persistent);
732 static int save_config(struct platform *p, struct config *config)
734 struct platform_powerpc *platform = to_platform_powerpc(p);
735 struct config *defaults;
738 defaults = talloc_zero(platform, struct config);
739 config_set_defaults(defaults);
741 rc = update_config(platform, config, defaults);
743 talloc_free(defaults);
747 static void finalise_config(struct platform *p, const struct config *config)
749 struct platform_powerpc *platform = to_platform_powerpc(p);
751 if (config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev)
752 platform->clear_ipmi_bootdev(platform);
755 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
757 struct platform_powerpc *platform = p->platform_data;
758 char *buf, *filename;
761 filename = talloc_asprintf(platform, "%smodel", devtree_dir);
762 rc = read_file(platform, filename, &buf, &len);
764 sysinfo->type = talloc_steal(sysinfo, buf);
765 talloc_free(filename);
767 filename = talloc_asprintf(platform, "%ssystem-id", devtree_dir);
768 rc = read_file(platform, filename, &buf, &len);
770 sysinfo->identifier = talloc_steal(sysinfo, buf);
771 talloc_free(filename);
776 static bool probe(struct platform *p, void *ctx)
778 struct platform_powerpc *platform;
782 /* we need a device tree */
783 rc = stat("/proc/device-tree", &statbuf);
787 if (!S_ISDIR(statbuf.st_mode))
790 platform = talloc(ctx, struct platform_powerpc);
791 list_init(&platform->params);
793 p->platform_data = platform;
795 if (!stat(sysparams_dir, &statbuf)) {
796 pb_debug("platform: using sysparams for IPMI paramters\n");
797 platform->get_ipmi_bootdev = get_ipmi_bootdev_sysparams;
798 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_sysparams;
801 pb_log("platform: no IPMI parameter support\n");
807 static struct platform platform_powerpc = {
809 .dhcp_arch_id = 0x000e,
811 .load_config = load_config,
812 .save_config = save_config,
813 .finalise_config = finalise_config,
814 .get_sysinfo = get_sysinfo,
817 register_platform(platform_powerpc);