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[] = {
40 #define to_platform_powerpc(p) \
41 (struct platform_powerpc *)(p->platform_data)
43 /* a partition max a max size of 64k * 16bytes = 1M */
44 static const int max_partition_size = 64 * 1024 * 16;
46 static bool param_is_known(const char *param, unsigned int len)
48 const char *known_param;
51 for (i = 0; known_params[i]; i++) {
52 known_param = known_params[i];
53 if (len == strlen(known_param) &&
54 !strncmp(param, known_param, len))
61 static int parse_nvram_params(struct platform_powerpc *platform,
64 char *pos, *name, *value;
65 unsigned int paramlen;
68 /* discard 2 header lines:
75 for (i = 0; i < len; i++) {
83 fprintf(stderr, "failure parsing nvram output\n");
87 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
92 newline = strchr(pos, '\n');
98 paramlen = strlen(pos);
101 value = strchr(pos, '=');
105 namelen = value - name;
109 if (!param_is_known(name, namelen))
114 param = talloc(platform, struct param);
115 param->modified = false;
116 param->name = talloc_strndup(platform, name, namelen);
117 param->value = talloc_strdup(platform, value);
118 list_add(&platform->params, ¶m->list);
124 static int parse_nvram(struct platform_powerpc *platform)
126 struct process *process;
131 argv[1] = "--print-config";
132 argv[2] = "--partition";
136 process = process_create(platform);
137 process->path = "nvram";
138 process->argv = argv;
139 process->keep_stdout = true;
141 rc = process_run_sync(process);
143 if (rc || !process_exit_ok(process)) {
144 fprintf(stderr, "nvram process returned "
145 "non-zero exit status\n");
148 rc = parse_nvram_params(platform, process->stdout_buf,
149 process->stdout_len);
152 process_release(process);
156 static int write_nvram(struct platform_powerpc *platform)
158 struct process *process;
164 argv[1] = "--update-config";
166 argv[3] = "--partition";
170 process = process_create(platform);
171 process->path = "nvram";
172 process->argv = argv;
174 list_for_each_entry(&platform->params, param, list) {
177 if (!param->modified)
180 paramstr = talloc_asprintf(platform, "%s=%s",
181 param->name, param->value);
184 rc = process_run_sync(process);
186 talloc_free(paramstr);
188 if (rc || !process_exit_ok(process)) {
190 pb_log("nvram update process returned "
191 "non-zero exit status\n");
196 process_release(process);
200 static const char *get_param(struct platform_powerpc *platform,
205 list_for_each_entry(&platform->params, param, list)
206 if (!strcmp(param->name, name))
211 static void set_param(struct platform_powerpc *platform, const char *name,
216 list_for_each_entry(&platform->params, param, list) {
217 if (strcmp(param->name, name))
220 if (!strcmp(param->value, value))
223 talloc_free(param->value);
224 param->value = talloc_strdup(param, value);
225 param->modified = true;
230 param = talloc(platform, struct param);
231 param->modified = true;
232 param->name = talloc_strdup(platform, name);
233 param->value = talloc_strdup(platform, value);
234 list_add(&platform->params, ¶m->list);
237 static int parse_hwaddr(struct interface_config *ifconf, char *str)
241 if (strlen(str) != strlen("00:00:00:00:00:00"))
244 for (i = 0; i < HWADDR_SIZE; i++) {
248 byte[0] = str[i * 3 + 0];
249 byte[1] = str[i * 3 + 1];
252 x = strtoul(byte, &endp, 16);
253 if (endp != byte + 2)
256 ifconf->hwaddr[i] = x & 0xff;
262 static int parse_one_interface_config(struct config *config,
265 struct interface_config *ifconf;
268 ifconf = talloc_zero(config, struct interface_config);
270 if (!confstr || !strlen(confstr))
273 /* first token should be the mac address */
274 tok = strtok_r(confstr, ",", &saveptr);
278 if (parse_hwaddr(ifconf, tok))
281 /* second token is the method */
282 tok = strtok_r(NULL, ",", &saveptr);
283 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
284 ifconf->ignore = true;
286 } else if (!strcmp(tok, "dhcp")) {
287 ifconf->method = CONFIG_METHOD_DHCP;
289 } else if (!strcmp(tok, "static")) {
290 ifconf->method = CONFIG_METHOD_STATIC;
292 /* ip/mask, [optional] gateway */
293 tok = strtok_r(NULL, ",", &saveptr);
296 ifconf->static_config.address =
297 talloc_strdup(ifconf, tok);
299 tok = strtok_r(NULL, ",", &saveptr);
301 ifconf->static_config.gateway =
302 talloc_strdup(ifconf, tok);
306 pb_log("Unknown network configuration method %s\n", tok);
310 config->network.interfaces = talloc_realloc(config,
311 config->network.interfaces,
312 struct interface_config *,
313 ++config->network.n_interfaces);
315 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
323 static int parse_one_dns_config(struct config *config,
328 for (tok = strtok_r(confstr, ",", &saveptr); tok;
329 tok = strtok_r(NULL, ",", &saveptr)) {
331 char *server = talloc_strdup(config, tok);
333 config->network.dns_servers = talloc_realloc(config,
334 config->network.dns_servers, const char *,
335 ++config->network.n_dns_servers);
337 config->network.dns_servers[config->network.n_dns_servers - 1]
344 static void populate_network_config(struct platform_powerpc *platform,
345 struct config *config)
351 cval = get_param(platform, "petitboot,network");
352 if (!cval || !strlen(cval))
355 val = talloc_strdup(config, cval);
360 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
364 if (!strncasecmp(tok, "dns,", strlen("dns,")))
365 parse_one_dns_config(config, tok + strlen("dns,"));
367 parse_one_interface_config(config, tok);
374 static void populate_config(struct platform_powerpc *platform,
375 struct config *config)
379 unsigned long timeout;
381 /* if the "auto-boot?' property is present and "false", disable auto
383 val = get_param(platform, "auto-boot?");
384 config->autoboot_enabled = !val || strcmp(val, "false");
386 val = get_param(platform, "petitboot,timeout");
388 timeout = strtoul(val, &end, 10);
390 if (timeout >= INT_MAX)
392 config->autoboot_timeout_sec = (int)timeout;
396 populate_network_config(platform, config);
399 static char *iface_config_str(void *ctx, struct interface_config *config)
403 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
404 * different hardware address formats */
405 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
406 config->hwaddr[0], config->hwaddr[1],
407 config->hwaddr[2], config->hwaddr[3],
408 config->hwaddr[4], config->hwaddr[5]);
410 if (config->ignore) {
411 str = talloc_asprintf_append(str, "ignore");
413 } else if (config->method == CONFIG_METHOD_DHCP) {
414 str = talloc_asprintf_append(str, "dhcp");
416 } else if (config->method == CONFIG_METHOD_STATIC) {
417 str = talloc_asprintf_append(str, "static,%s%s%s",
418 config->static_config.address,
419 config->static_config.gateway ? "," : "",
420 config->static_config.gateway ?: "");
425 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
430 str = talloc_strdup(ctx, "dns,");
431 for (i = 0; i < n; i++) {
432 str = talloc_asprintf_append(str, "%s%s",
440 static void update_string_config(struct platform_powerpc *platform,
441 const char *name, const char *value)
445 cur = get_param(platform, name);
447 /* don't set an empty parameter if it doesn't already exist */
448 if (!cur && !strlen(value))
451 set_param(platform, name, value);
454 static void update_network_config(struct platform_powerpc *platform,
455 struct config *config)
460 val = talloc_strdup(platform, "");
462 for (i = 0; i < config->network.n_interfaces; i++) {
463 char *iface_str = iface_config_str(platform,
464 config->network.interfaces[i]);
465 val = talloc_asprintf_append(val, "%s%s",
466 *val == '\0' ? "" : " ", iface_str);
467 talloc_free(iface_str);
470 if (config->network.n_dns_servers) {
471 char *dns_str = dns_config_str(platform,
472 config->network.dns_servers,
473 config->network.n_dns_servers);
474 val = talloc_asprintf_append(val, "%s%s",
475 *val == '\0' ? "" : " ", dns_str);
476 talloc_free(dns_str);
479 update_string_config(platform, "petitboot,network", val);
484 static int update_config(struct platform_powerpc *platform,
485 struct config *config, struct config *defaults)
490 if (config->autoboot_enabled == defaults->autoboot_enabled)
493 val = config->autoboot_enabled ? "true" : "false";
494 update_string_config(platform, "auto-boot?", val);
496 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
499 val = tmp = talloc_asprintf(platform, "%d",
500 config->autoboot_timeout_sec);
502 update_string_config(platform, "petitboot,timeout", val);
506 update_network_config(platform, config);
508 return write_nvram(platform);
511 static void set_exclusive_devtype(struct config *config,
512 enum device_type devtype)
514 config->n_boot_priorities = 2;
515 config->boot_priorities = talloc_realloc(config,
516 config->boot_priorities, struct boot_priority,
517 config->n_boot_priorities);
518 config->boot_priorities[0].type = devtype;
519 config->boot_priorities[0].priority = 0;
520 config->boot_priorities[1].type = DEVICE_TYPE_ANY;
521 config->boot_priorities[1].priority = -1;
524 /* bootdev options that we recognise */
526 IPMI_BOOTDEV_NONE = 0x00,
527 IPMI_BOOTDEV_NETWORK = 0x01,
528 IPMI_BOOTDEV_DISK = 0x2,
529 IPMI_BOOTDEV_CDROM = 0x5,
530 IPMI_BOOTDEV_SETUP = 0x6,
533 static int read_bootdev_sysparam(const char *name, uint8_t *val)
539 strcpy(path, sysparams_dir);
540 assert(strlen(name) < sizeof(path) - strlen(path));
543 fd = open(path, O_RDONLY);
545 pb_debug("powerpc: can't access sysparam %s\n",
550 rc = read(fd, buf, sizeof(buf));
554 /* bootdev definitions should only be one byte in size */
556 pb_debug("powerpc: sysparam %s read returned %d\n",
561 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
566 case IPMI_BOOTDEV_NONE:
567 case IPMI_BOOTDEV_NETWORK:
568 case IPMI_BOOTDEV_DISK:
569 case IPMI_BOOTDEV_CDROM:
570 case IPMI_BOOTDEV_SETUP:
577 static int write_bootdev_sysparam(const char *name, uint8_t val)
582 strcpy(path, sysparams_dir);
583 assert(strlen(name) < sizeof(path) - strlen(path));
586 fd = open(path, O_WRONLY);
588 pb_debug("powerpc: can't access sysparam %s for writing\n",
595 rc = write(fd, &val, sizeof(val));
596 if (rc == sizeof(val)) {
601 if (rc <= 0 && errno != EINTR) {
602 pb_log("powerpc: error updating sysparam %s: %s",
603 name, strerror(errno));
612 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
617 static void parse_opal_sysparams(struct config *config)
619 uint8_t next_bootdev, default_bootdev;
620 bool next_valid, default_valid;
623 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
624 next_valid = rc == 0;
626 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
627 default_valid = rc == 0;
629 /* nothing valid? no need to change the config */
630 if (!next_valid && !default_valid)
634 /* invalidate next-boot-device setting */
635 write_bootdev_sysparam("next-boot-device", 0xff);
637 next_bootdev = default_bootdev;
640 switch (next_bootdev) {
641 case IPMI_BOOTDEV_NONE:
643 case IPMI_BOOTDEV_DISK:
644 set_exclusive_devtype(config, DEVICE_TYPE_DISK);
646 case IPMI_BOOTDEV_NETWORK:
647 set_exclusive_devtype(config, DEVICE_TYPE_NETWORK);
649 case IPMI_BOOTDEV_CDROM:
650 set_exclusive_devtype(config, DEVICE_TYPE_OPTICAL);
652 case IPMI_BOOTDEV_SETUP:
653 config->autoboot_enabled = false;
658 static int load_config(struct platform *p, struct config *config)
660 struct platform_powerpc *platform = to_platform_powerpc(p);
663 rc = parse_nvram(platform);
667 populate_config(platform, config);
669 parse_opal_sysparams(config);
674 static int save_config(struct platform *p, struct config *config)
676 struct platform_powerpc *platform = to_platform_powerpc(p);
677 struct config *defaults;
680 defaults = talloc_zero(platform, struct config);
681 config_set_defaults(defaults);
683 rc = update_config(platform, config, defaults);
685 talloc_free(defaults);
689 static bool probe(struct platform *p, void *ctx)
691 struct platform_powerpc *platform;
695 /* we need a device tree and a working nvram binary */
696 rc = stat("/proc/device-tree", &statbuf);
700 if (!S_ISDIR(statbuf.st_mode))
703 rc = process_run_simple(ctx, "nvram", "--print-config", NULL);
704 if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0)
707 platform = talloc(ctx, struct platform_powerpc);
708 list_init(&platform->params);
710 p->platform_data = platform;
714 static struct platform platform_powerpc = {
716 .dhcp_arch_id = 0x000e,
718 .load_config = load_config,
719 .save_config = save_config,
722 register_platform(platform_powerpc);