12 #include <file/file.h>
13 #include <talloc/talloc.h>
14 #include <list/list.h>
16 #include <process/process.h>
20 static const char *partition = "common";
21 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
22 static const char *devtree_dir = "/proc/device-tree/";
28 struct list_item list;
31 struct platform_powerpc {
35 static const char *known_params[] = {
45 #define to_platform_powerpc(p) \
46 (struct platform_powerpc *)(p->platform_data)
48 /* a partition max a max size of 64k * 16bytes = 1M */
49 static const int max_partition_size = 64 * 1024 * 16;
51 static bool param_is_known(const char *param, unsigned int len)
53 const char *known_param;
56 for (i = 0; known_params[i]; i++) {
57 known_param = known_params[i];
58 if (len == strlen(known_param) &&
59 !strncmp(param, known_param, len))
66 static int parse_nvram_params(struct platform_powerpc *platform,
69 char *pos, *name, *value;
70 unsigned int paramlen;
73 /* discard 2 header lines:
80 for (i = 0; i < len; i++) {
88 fprintf(stderr, "failure parsing nvram output\n");
92 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
97 newline = strchr(pos, '\n');
103 paramlen = strlen(pos);
106 value = strchr(pos, '=');
110 namelen = value - name;
114 if (!param_is_known(name, namelen))
119 param = talloc(platform, struct param);
120 param->modified = false;
121 param->name = talloc_strndup(platform, name, namelen);
122 param->value = talloc_strdup(platform, value);
123 list_add(&platform->params, ¶m->list);
129 static int parse_nvram(struct platform_powerpc *platform)
131 struct process *process;
136 argv[1] = "--print-config";
137 argv[2] = "--partition";
141 process = process_create(platform);
142 process->path = "nvram";
143 process->argv = argv;
144 process->keep_stdout = true;
146 rc = process_run_sync(process);
148 if (rc || !process_exit_ok(process)) {
149 fprintf(stderr, "nvram process returned "
150 "non-zero exit status\n");
153 rc = parse_nvram_params(platform, process->stdout_buf,
154 process->stdout_len);
157 process_release(process);
161 static int write_nvram(struct platform_powerpc *platform)
163 struct process *process;
169 argv[1] = "--update-config";
171 argv[3] = "--partition";
175 process = process_create(platform);
176 process->path = "nvram";
177 process->argv = argv;
179 list_for_each_entry(&platform->params, param, list) {
182 if (!param->modified)
185 paramstr = talloc_asprintf(platform, "%s=%s",
186 param->name, param->value);
189 rc = process_run_sync(process);
191 talloc_free(paramstr);
193 if (rc || !process_exit_ok(process)) {
195 pb_log("nvram update process returned "
196 "non-zero exit status\n");
201 process_release(process);
205 static const char *get_param(struct platform_powerpc *platform,
210 list_for_each_entry(&platform->params, param, list)
211 if (!strcmp(param->name, name))
216 static void set_param(struct platform_powerpc *platform, const char *name,
221 list_for_each_entry(&platform->params, param, list) {
222 if (strcmp(param->name, name))
225 if (!strcmp(param->value, value))
228 talloc_free(param->value);
229 param->value = talloc_strdup(param, value);
230 param->modified = true;
235 param = talloc(platform, struct param);
236 param->modified = true;
237 param->name = talloc_strdup(platform, name);
238 param->value = talloc_strdup(platform, value);
239 list_add(&platform->params, ¶m->list);
242 static int parse_hwaddr(struct interface_config *ifconf, char *str)
246 if (strlen(str) != strlen("00:00:00:00:00:00"))
249 for (i = 0; i < HWADDR_SIZE; i++) {
253 byte[0] = str[i * 3 + 0];
254 byte[1] = str[i * 3 + 1];
257 x = strtoul(byte, &endp, 16);
258 if (endp != byte + 2)
261 ifconf->hwaddr[i] = x & 0xff;
267 static int parse_one_interface_config(struct config *config,
270 struct interface_config *ifconf;
273 ifconf = talloc_zero(config, struct interface_config);
275 if (!confstr || !strlen(confstr))
278 /* first token should be the mac address */
279 tok = strtok_r(confstr, ",", &saveptr);
283 if (parse_hwaddr(ifconf, tok))
286 /* second token is the method */
287 tok = strtok_r(NULL, ",", &saveptr);
288 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
289 ifconf->ignore = true;
291 } else if (!strcmp(tok, "dhcp")) {
292 ifconf->method = CONFIG_METHOD_DHCP;
294 } else if (!strcmp(tok, "static")) {
295 ifconf->method = CONFIG_METHOD_STATIC;
297 /* ip/mask, [optional] gateway */
298 tok = strtok_r(NULL, ",", &saveptr);
301 ifconf->static_config.address =
302 talloc_strdup(ifconf, tok);
304 tok = strtok_r(NULL, ",", &saveptr);
306 ifconf->static_config.gateway =
307 talloc_strdup(ifconf, tok);
311 pb_log("Unknown network configuration method %s\n", tok);
315 config->network.interfaces = talloc_realloc(config,
316 config->network.interfaces,
317 struct interface_config *,
318 ++config->network.n_interfaces);
320 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
328 static int parse_one_dns_config(struct config *config,
333 for (tok = strtok_r(confstr, ",", &saveptr); tok;
334 tok = strtok_r(NULL, ",", &saveptr)) {
336 char *server = talloc_strdup(config, tok);
338 config->network.dns_servers = talloc_realloc(config,
339 config->network.dns_servers, const char *,
340 ++config->network.n_dns_servers);
342 config->network.dns_servers[config->network.n_dns_servers - 1]
349 static void populate_network_config(struct platform_powerpc *platform,
350 struct config *config)
352 char *val, *saveptr = NULL;
356 cval = get_param(platform, "petitboot,network");
357 if (!cval || !strlen(cval))
360 val = talloc_strdup(config, cval);
365 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
369 if (!strncasecmp(tok, "dns,", strlen("dns,")))
370 parse_one_dns_config(config, tok + strlen("dns,"));
372 parse_one_interface_config(config, tok);
379 static void populate_bootdev_config(struct platform_powerpc *platform,
380 struct config *config)
385 config->boot_device = NULL;
387 val = get_param(platform, "petitboot,bootdev");
388 if (!val || !strlen(val))
391 if (!strncmp(val, "uuid:", strlen("uuid:"))) {
392 config->boot_device = talloc_strdup(config,
393 val + strlen("uuid:"));
395 } else if (!strncmp(val, "mac:", strlen("mac:"))) {
396 config->boot_device = talloc_strdup(config,
397 val + strlen("mac:"));
400 pb_log("bootdev config is in an unknown format "
401 "(expected uuid:... or mac:...)");
405 static void populate_config(struct platform_powerpc *platform,
406 struct config *config)
410 unsigned long timeout;
412 /* if the "auto-boot?' property is present and "false", disable auto
414 val = get_param(platform, "auto-boot?");
415 config->autoboot_enabled = !val || strcmp(val, "false");
417 val = get_param(platform, "petitboot,timeout");
419 timeout = strtoul(val, &end, 10);
421 if (timeout >= INT_MAX)
423 config->autoboot_timeout_sec = (int)timeout;
427 val = get_param(platform, "petitboot,language");
428 config->lang = val ? talloc_strdup(config, val) : NULL;
430 populate_network_config(platform, config);
432 populate_bootdev_config(platform, config);
434 if (!config->debug) {
435 val = get_param(platform, "petitboot,debug?");
436 config->debug = val && !strcmp(val, "true");
440 static char *iface_config_str(void *ctx, struct interface_config *config)
444 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
445 * different hardware address formats */
446 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
447 config->hwaddr[0], config->hwaddr[1],
448 config->hwaddr[2], config->hwaddr[3],
449 config->hwaddr[4], config->hwaddr[5]);
451 if (config->ignore) {
452 str = talloc_asprintf_append(str, "ignore");
454 } else if (config->method == CONFIG_METHOD_DHCP) {
455 str = talloc_asprintf_append(str, "dhcp");
457 } else if (config->method == CONFIG_METHOD_STATIC) {
458 str = talloc_asprintf_append(str, "static,%s%s%s",
459 config->static_config.address,
460 config->static_config.gateway ? "," : "",
461 config->static_config.gateway ?: "");
466 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
471 str = talloc_strdup(ctx, "dns,");
472 for (i = 0; i < n; i++) {
473 str = talloc_asprintf_append(str, "%s%s",
481 static void update_string_config(struct platform_powerpc *platform,
482 const char *name, const char *value)
486 cur = get_param(platform, name);
488 /* don't set an empty parameter if it doesn't already exist */
489 if (!cur && !strlen(value))
492 set_param(platform, name, value);
495 static void update_network_config(struct platform_powerpc *platform,
496 struct config *config)
501 val = talloc_strdup(platform, "");
503 for (i = 0; i < config->network.n_interfaces; i++) {
504 char *iface_str = iface_config_str(platform,
505 config->network.interfaces[i]);
506 val = talloc_asprintf_append(val, "%s%s",
507 *val == '\0' ? "" : " ", iface_str);
508 talloc_free(iface_str);
511 if (config->network.n_dns_servers) {
512 char *dns_str = dns_config_str(platform,
513 config->network.dns_servers,
514 config->network.n_dns_servers);
515 val = talloc_asprintf_append(val, "%s%s",
516 *val == '\0' ? "" : " ", dns_str);
517 talloc_free(dns_str);
520 update_string_config(platform, "petitboot,network", val);
525 static void update_bootdev_config(struct platform_powerpc *platform,
526 struct config *config)
528 char *val, *tmp = NULL;
530 if (!config->boot_device)
533 tmp = val = talloc_asprintf(platform,
534 "uuid:%s", config->boot_device);
536 update_string_config(platform, "petitboot,bootdev", val);
540 static int update_config(struct platform_powerpc *platform,
541 struct config *config, struct config *defaults)
546 if (config->autoboot_enabled == defaults->autoboot_enabled)
549 val = config->autoboot_enabled ? "true" : "false";
550 update_string_config(platform, "auto-boot?", val);
552 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
555 val = tmp = talloc_asprintf(platform, "%d",
556 config->autoboot_timeout_sec);
558 update_string_config(platform, "petitboot,timeout", val);
562 val = config->lang ?: "";
563 update_string_config(platform, "petitboot,language", val);
565 update_network_config(platform, config);
567 update_bootdev_config(platform, config);
569 return write_nvram(platform);
572 static void set_exclusive_devtype(struct config *config,
573 enum device_type devtype)
575 config->n_boot_priorities = 2;
576 config->boot_priorities = talloc_realloc(config,
577 config->boot_priorities, struct boot_priority,
578 config->n_boot_priorities);
579 config->boot_priorities[0].type = devtype;
580 config->boot_priorities[0].priority = 0;
581 config->boot_priorities[1].type = DEVICE_TYPE_ANY;
582 config->boot_priorities[1].priority = -1;
585 /* bootdev options that we recognise */
587 IPMI_BOOTDEV_NONE = 0x00,
588 IPMI_BOOTDEV_NETWORK = 0x01,
589 IPMI_BOOTDEV_DISK = 0x2,
590 IPMI_BOOTDEV_SAFE = 0x3,
591 IPMI_BOOTDEV_CDROM = 0x5,
592 IPMI_BOOTDEV_SETUP = 0x6,
595 static int read_bootdev_sysparam(const char *name, uint8_t *val)
601 strcpy(path, sysparams_dir);
602 assert(strlen(name) < sizeof(path) - strlen(path));
605 fd = open(path, O_RDONLY);
607 pb_debug("powerpc: can't access sysparam %s\n",
612 rc = read(fd, buf, sizeof(buf));
616 /* bootdev definitions should only be one byte in size */
618 pb_debug("powerpc: sysparam %s read returned %d\n",
623 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
628 case IPMI_BOOTDEV_NONE:
629 case IPMI_BOOTDEV_NETWORK:
630 case IPMI_BOOTDEV_DISK:
631 case IPMI_BOOTDEV_SAFE:
632 case IPMI_BOOTDEV_CDROM:
633 case IPMI_BOOTDEV_SETUP:
640 static int write_bootdev_sysparam(const char *name, uint8_t val)
645 strcpy(path, sysparams_dir);
646 assert(strlen(name) < sizeof(path) - strlen(path));
649 fd = open(path, O_WRONLY);
651 pb_debug("powerpc: can't access sysparam %s for writing\n",
658 rc = write(fd, &val, sizeof(val));
659 if (rc == sizeof(val)) {
664 if (rc <= 0 && errno != EINTR) {
665 pb_log("powerpc: error updating sysparam %s: %s",
666 name, strerror(errno));
675 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
680 static void parse_opal_sysparams(struct config *config)
682 uint8_t next_bootdev, default_bootdev;
683 bool next_valid, default_valid;
686 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
687 next_valid = rc == 0;
689 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
690 default_valid = rc == 0;
692 /* nothing valid? no need to change the config */
693 if (!next_valid && !default_valid)
697 next_bootdev = default_bootdev;
699 switch (next_bootdev) {
700 case IPMI_BOOTDEV_NONE:
702 case IPMI_BOOTDEV_DISK:
703 set_exclusive_devtype(config, DEVICE_TYPE_DISK);
705 case IPMI_BOOTDEV_NETWORK:
706 set_exclusive_devtype(config, DEVICE_TYPE_NETWORK);
708 case IPMI_BOOTDEV_CDROM:
709 set_exclusive_devtype(config, DEVICE_TYPE_OPTICAL);
711 case IPMI_BOOTDEV_SETUP:
712 config->autoboot_enabled = false;
714 case IPMI_BOOTDEV_SAFE:
715 config->autoboot_enabled = false;
716 config->safe_mode = true;
721 static int load_config(struct platform *p, struct config *config)
723 struct platform_powerpc *platform = to_platform_powerpc(p);
726 rc = parse_nvram(platform);
730 populate_config(platform, config);
732 parse_opal_sysparams(config);
737 static int save_config(struct platform *p, struct config *config)
739 struct platform_powerpc *platform = to_platform_powerpc(p);
740 struct config *defaults;
743 defaults = talloc_zero(platform, struct config);
744 config_set_defaults(defaults);
746 rc = update_config(platform, config, defaults);
748 talloc_free(defaults);
752 static void finalise_config(struct platform *platform __attribute__((unused)))
754 /* invalidate next-boot-device setting */
755 write_bootdev_sysparam("next-boot-device", 0xff);
758 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
760 struct platform_powerpc *platform = p->platform_data;
761 char *buf, *filename;
764 filename = talloc_asprintf(platform, "%smodel", devtree_dir);
765 rc = read_file(platform, filename, &buf, &len);
767 sysinfo->type = talloc_steal(sysinfo, buf);
768 talloc_free(filename);
770 filename = talloc_asprintf(platform, "%ssystem-id", devtree_dir);
771 rc = read_file(platform, filename, &buf, &len);
773 sysinfo->identifier = talloc_steal(sysinfo, buf);
774 talloc_free(filename);
779 static bool probe(struct platform *p, void *ctx)
781 struct platform_powerpc *platform;
785 /* we need a device tree */
786 rc = stat("/proc/device-tree", &statbuf);
790 if (!S_ISDIR(statbuf.st_mode))
793 platform = talloc(ctx, struct platform_powerpc);
794 list_init(&platform->params);
796 p->platform_data = platform;
801 static struct platform platform_powerpc = {
803 .dhcp_arch_id = 0x000e,
805 .load_config = load_config,
806 .save_config = save_config,
807 .finalise_config = finalise_config,
808 .get_sysinfo = get_sysinfo,
811 register_platform(platform_powerpc);