12 #include <talloc/talloc.h>
13 #include <list/list.h>
15 #include <process/process.h>
19 static const char *partition = "common";
20 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
26 struct list_item list;
29 struct platform_powerpc {
33 static const char *known_params[] = {
41 #define to_platform_powerpc(p) \
42 (struct platform_powerpc *)(p->platform_data)
44 /* a partition max a max size of 64k * 16bytes = 1M */
45 static const int max_partition_size = 64 * 1024 * 16;
47 static bool param_is_known(const char *param, unsigned int len)
49 const char *known_param;
52 for (i = 0; known_params[i]; i++) {
53 known_param = known_params[i];
54 if (len == strlen(known_param) &&
55 !strncmp(param, known_param, len))
62 static int parse_nvram_params(struct platform_powerpc *platform,
65 char *pos, *name, *value;
66 unsigned int paramlen;
69 /* discard 2 header lines:
76 for (i = 0; i < len; i++) {
84 fprintf(stderr, "failure parsing nvram output\n");
88 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
93 newline = strchr(pos, '\n');
99 paramlen = strlen(pos);
102 value = strchr(pos, '=');
106 namelen = value - name;
110 if (!param_is_known(name, namelen))
115 param = talloc(platform, struct param);
116 param->modified = false;
117 param->name = talloc_strndup(platform, name, namelen);
118 param->value = talloc_strdup(platform, value);
119 list_add(&platform->params, ¶m->list);
125 static int parse_nvram(struct platform_powerpc *platform)
127 struct process *process;
132 argv[1] = "--print-config";
133 argv[2] = "--partition";
137 process = process_create(platform);
138 process->path = "nvram";
139 process->argv = argv;
140 process->keep_stdout = true;
142 rc = process_run_sync(process);
144 if (rc || !process_exit_ok(process)) {
145 fprintf(stderr, "nvram process returned "
146 "non-zero exit status\n");
149 rc = parse_nvram_params(platform, process->stdout_buf,
150 process->stdout_len);
153 process_release(process);
157 static int write_nvram(struct platform_powerpc *platform)
159 struct process *process;
165 argv[1] = "--update-config";
167 argv[3] = "--partition";
171 process = process_create(platform);
172 process->path = "nvram";
173 process->argv = argv;
175 list_for_each_entry(&platform->params, param, list) {
178 if (!param->modified)
181 paramstr = talloc_asprintf(platform, "%s=%s",
182 param->name, param->value);
185 rc = process_run_sync(process);
187 talloc_free(paramstr);
189 if (rc || !process_exit_ok(process)) {
191 pb_log("nvram update process returned "
192 "non-zero exit status\n");
197 process_release(process);
201 static const char *get_param(struct platform_powerpc *platform,
206 list_for_each_entry(&platform->params, param, list)
207 if (!strcmp(param->name, name))
212 static void set_param(struct platform_powerpc *platform, const char *name,
217 list_for_each_entry(&platform->params, param, list) {
218 if (strcmp(param->name, name))
221 if (!strcmp(param->value, value))
224 talloc_free(param->value);
225 param->value = talloc_strdup(param, value);
226 param->modified = true;
231 param = talloc(platform, struct param);
232 param->modified = true;
233 param->name = talloc_strdup(platform, name);
234 param->value = talloc_strdup(platform, value);
235 list_add(&platform->params, ¶m->list);
238 static int parse_hwaddr(struct interface_config *ifconf, char *str)
242 if (strlen(str) != strlen("00:00:00:00:00:00"))
245 for (i = 0; i < HWADDR_SIZE; i++) {
249 byte[0] = str[i * 3 + 0];
250 byte[1] = str[i * 3 + 1];
253 x = strtoul(byte, &endp, 16);
254 if (endp != byte + 2)
257 ifconf->hwaddr[i] = x & 0xff;
263 static int parse_one_interface_config(struct config *config,
266 struct interface_config *ifconf;
269 ifconf = talloc_zero(config, struct interface_config);
271 if (!confstr || !strlen(confstr))
274 /* first token should be the mac address */
275 tok = strtok_r(confstr, ",", &saveptr);
279 if (parse_hwaddr(ifconf, tok))
282 /* second token is the method */
283 tok = strtok_r(NULL, ",", &saveptr);
284 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
285 ifconf->ignore = true;
287 } else if (!strcmp(tok, "dhcp")) {
288 ifconf->method = CONFIG_METHOD_DHCP;
290 } else if (!strcmp(tok, "static")) {
291 ifconf->method = CONFIG_METHOD_STATIC;
293 /* ip/mask, [optional] gateway */
294 tok = strtok_r(NULL, ",", &saveptr);
297 ifconf->static_config.address =
298 talloc_strdup(ifconf, tok);
300 tok = strtok_r(NULL, ",", &saveptr);
302 ifconf->static_config.gateway =
303 talloc_strdup(ifconf, tok);
307 pb_log("Unknown network configuration method %s\n", tok);
311 config->network.interfaces = talloc_realloc(config,
312 config->network.interfaces,
313 struct interface_config *,
314 ++config->network.n_interfaces);
316 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
324 static int parse_one_dns_config(struct config *config,
329 for (tok = strtok_r(confstr, ",", &saveptr); tok;
330 tok = strtok_r(NULL, ",", &saveptr)) {
332 char *server = talloc_strdup(config, tok);
334 config->network.dns_servers = talloc_realloc(config,
335 config->network.dns_servers, const char *,
336 ++config->network.n_dns_servers);
338 config->network.dns_servers[config->network.n_dns_servers - 1]
345 static void populate_network_config(struct platform_powerpc *platform,
346 struct config *config)
348 char *val, *saveptr = NULL;
352 cval = get_param(platform, "petitboot,network");
353 if (!cval || !strlen(cval))
356 val = talloc_strdup(config, cval);
361 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
365 if (!strncasecmp(tok, "dns,", strlen("dns,")))
366 parse_one_dns_config(config, tok + strlen("dns,"));
368 parse_one_interface_config(config, tok);
375 static void populate_bootdev_config(struct platform_powerpc *platform,
376 struct config *config)
381 config->boot_device = NULL;
383 val = get_param(platform, "petitboot,bootdev");
384 if (!val || !strlen(val))
387 if (!strncmp(val, "uuid:", strlen("uuid:"))) {
388 config->boot_device = talloc_strdup(config,
389 val + strlen("uuid:"));
391 } else if (!strncmp(val, "mac:", strlen("mac:"))) {
392 config->boot_device = talloc_strdup(config,
393 val + strlen("mac:"));
396 pb_log("bootdev config is in an unknown format "
397 "(expected uuid:... or mac:...)");
401 static void populate_config(struct platform_powerpc *platform,
402 struct config *config)
406 unsigned long timeout;
408 /* if the "auto-boot?' property is present and "false", disable auto
410 val = get_param(platform, "auto-boot?");
411 config->autoboot_enabled = !val || strcmp(val, "false");
413 val = get_param(platform, "petitboot,timeout");
415 timeout = strtoul(val, &end, 10);
417 if (timeout >= INT_MAX)
419 config->autoboot_timeout_sec = (int)timeout;
423 populate_network_config(platform, config);
425 populate_bootdev_config(platform, config);
428 static char *iface_config_str(void *ctx, struct interface_config *config)
432 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
433 * different hardware address formats */
434 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
435 config->hwaddr[0], config->hwaddr[1],
436 config->hwaddr[2], config->hwaddr[3],
437 config->hwaddr[4], config->hwaddr[5]);
439 if (config->ignore) {
440 str = talloc_asprintf_append(str, "ignore");
442 } else if (config->method == CONFIG_METHOD_DHCP) {
443 str = talloc_asprintf_append(str, "dhcp");
445 } else if (config->method == CONFIG_METHOD_STATIC) {
446 str = talloc_asprintf_append(str, "static,%s%s%s",
447 config->static_config.address,
448 config->static_config.gateway ? "," : "",
449 config->static_config.gateway ?: "");
454 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
459 str = talloc_strdup(ctx, "dns,");
460 for (i = 0; i < n; i++) {
461 str = talloc_asprintf_append(str, "%s%s",
469 static void update_string_config(struct platform_powerpc *platform,
470 const char *name, const char *value)
474 cur = get_param(platform, name);
476 /* don't set an empty parameter if it doesn't already exist */
477 if (!cur && !strlen(value))
480 set_param(platform, name, value);
483 static void update_network_config(struct platform_powerpc *platform,
484 struct config *config)
489 val = talloc_strdup(platform, "");
491 for (i = 0; i < config->network.n_interfaces; i++) {
492 char *iface_str = iface_config_str(platform,
493 config->network.interfaces[i]);
494 val = talloc_asprintf_append(val, "%s%s",
495 *val == '\0' ? "" : " ", iface_str);
496 talloc_free(iface_str);
499 if (config->network.n_dns_servers) {
500 char *dns_str = dns_config_str(platform,
501 config->network.dns_servers,
502 config->network.n_dns_servers);
503 val = talloc_asprintf_append(val, "%s%s",
504 *val == '\0' ? "" : " ", dns_str);
505 talloc_free(dns_str);
508 update_string_config(platform, "petitboot,network", val);
513 static void update_bootdev_config(struct platform_powerpc *platform,
514 struct config *config)
516 char *val, *tmp = NULL;
518 if (!config->boot_device)
521 tmp = val = talloc_asprintf(platform,
522 "uuid:%s", config->boot_device);
524 update_string_config(platform, "petitboot,bootdev", val);
528 static int update_config(struct platform_powerpc *platform,
529 struct config *config, struct config *defaults)
534 if (config->autoboot_enabled == defaults->autoboot_enabled)
537 val = config->autoboot_enabled ? "true" : "false";
538 update_string_config(platform, "auto-boot?", val);
540 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
543 val = tmp = talloc_asprintf(platform, "%d",
544 config->autoboot_timeout_sec);
546 update_string_config(platform, "petitboot,timeout", val);
550 update_network_config(platform, config);
552 update_bootdev_config(platform, config);
554 return write_nvram(platform);
557 static void set_exclusive_devtype(struct config *config,
558 enum device_type devtype)
560 config->n_boot_priorities = 2;
561 config->boot_priorities = talloc_realloc(config,
562 config->boot_priorities, struct boot_priority,
563 config->n_boot_priorities);
564 config->boot_priorities[0].type = devtype;
565 config->boot_priorities[0].priority = 0;
566 config->boot_priorities[1].type = DEVICE_TYPE_ANY;
567 config->boot_priorities[1].priority = -1;
570 /* bootdev options that we recognise */
572 IPMI_BOOTDEV_NONE = 0x00,
573 IPMI_BOOTDEV_NETWORK = 0x01,
574 IPMI_BOOTDEV_DISK = 0x2,
575 IPMI_BOOTDEV_SAFE = 0x3,
576 IPMI_BOOTDEV_CDROM = 0x5,
577 IPMI_BOOTDEV_SETUP = 0x6,
580 static int read_bootdev_sysparam(const char *name, uint8_t *val)
586 strcpy(path, sysparams_dir);
587 assert(strlen(name) < sizeof(path) - strlen(path));
590 fd = open(path, O_RDONLY);
592 pb_debug("powerpc: can't access sysparam %s\n",
597 rc = read(fd, buf, sizeof(buf));
601 /* bootdev definitions should only be one byte in size */
603 pb_debug("powerpc: sysparam %s read returned %d\n",
608 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
613 case IPMI_BOOTDEV_NONE:
614 case IPMI_BOOTDEV_NETWORK:
615 case IPMI_BOOTDEV_DISK:
616 case IPMI_BOOTDEV_SAFE:
617 case IPMI_BOOTDEV_CDROM:
618 case IPMI_BOOTDEV_SETUP:
625 static int write_bootdev_sysparam(const char *name, uint8_t val)
630 strcpy(path, sysparams_dir);
631 assert(strlen(name) < sizeof(path) - strlen(path));
634 fd = open(path, O_WRONLY);
636 pb_debug("powerpc: can't access sysparam %s for writing\n",
643 rc = write(fd, &val, sizeof(val));
644 if (rc == sizeof(val)) {
649 if (rc <= 0 && errno != EINTR) {
650 pb_log("powerpc: error updating sysparam %s: %s",
651 name, strerror(errno));
660 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
665 static void parse_opal_sysparams(struct config *config)
667 uint8_t next_bootdev, default_bootdev;
668 bool next_valid, default_valid;
671 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
672 next_valid = rc == 0;
674 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
675 default_valid = rc == 0;
677 /* nothing valid? no need to change the config */
678 if (!next_valid && !default_valid)
682 /* invalidate next-boot-device setting */
683 write_bootdev_sysparam("next-boot-device", 0xff);
685 next_bootdev = default_bootdev;
688 switch (next_bootdev) {
689 case IPMI_BOOTDEV_NONE:
691 case IPMI_BOOTDEV_DISK:
692 set_exclusive_devtype(config, DEVICE_TYPE_DISK);
694 case IPMI_BOOTDEV_NETWORK:
695 set_exclusive_devtype(config, DEVICE_TYPE_NETWORK);
697 case IPMI_BOOTDEV_CDROM:
698 set_exclusive_devtype(config, DEVICE_TYPE_OPTICAL);
700 case IPMI_BOOTDEV_SETUP:
701 config->autoboot_enabled = false;
703 case IPMI_BOOTDEV_SAFE:
704 config->autoboot_enabled = false;
705 config->safe_mode = true;
710 static int load_config(struct platform *p, struct config *config)
712 struct platform_powerpc *platform = to_platform_powerpc(p);
715 rc = parse_nvram(platform);
719 populate_config(platform, config);
721 parse_opal_sysparams(config);
726 static int save_config(struct platform *p, struct config *config)
728 struct platform_powerpc *platform = to_platform_powerpc(p);
729 struct config *defaults;
732 defaults = talloc_zero(platform, struct config);
733 config_set_defaults(defaults);
735 rc = update_config(platform, config, defaults);
737 talloc_free(defaults);
741 static bool probe(struct platform *p, void *ctx)
743 struct platform_powerpc *platform;
747 /* we need a device tree */
748 rc = stat("/proc/device-tree", &statbuf);
752 if (!S_ISDIR(statbuf.st_mode))
755 platform = talloc(ctx, struct platform_powerpc);
756 list_init(&platform->params);
758 p->platform_data = platform;
762 static struct platform platform_powerpc = {
764 .dhcp_arch_id = 0x000e,
766 .load_config = load_config,
767 .save_config = save_config,
770 register_platform(platform_powerpc);