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[] = {
44 #define to_platform_powerpc(p) \
45 (struct platform_powerpc *)(p->platform_data)
47 /* a partition max a max size of 64k * 16bytes = 1M */
48 static const int max_partition_size = 64 * 1024 * 16;
50 static bool param_is_known(const char *param, unsigned int len)
52 const char *known_param;
55 for (i = 0; known_params[i]; i++) {
56 known_param = known_params[i];
57 if (len == strlen(known_param) &&
58 !strncmp(param, known_param, len))
65 static int parse_nvram_params(struct platform_powerpc *platform,
68 char *pos, *name, *value;
69 unsigned int paramlen;
72 /* discard 2 header lines:
79 for (i = 0; i < len; i++) {
87 fprintf(stderr, "failure parsing nvram output\n");
91 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
96 newline = strchr(pos, '\n');
102 paramlen = strlen(pos);
105 value = strchr(pos, '=');
109 namelen = value - name;
113 if (!param_is_known(name, namelen))
118 param = talloc(platform, struct param);
119 param->modified = false;
120 param->name = talloc_strndup(platform, name, namelen);
121 param->value = talloc_strdup(platform, value);
122 list_add(&platform->params, ¶m->list);
128 static int parse_nvram(struct platform_powerpc *platform)
130 struct process *process;
135 argv[1] = "--print-config";
136 argv[2] = "--partition";
140 process = process_create(platform);
141 process->path = "nvram";
142 process->argv = argv;
143 process->keep_stdout = true;
145 rc = process_run_sync(process);
147 if (rc || !process_exit_ok(process)) {
148 fprintf(stderr, "nvram process returned "
149 "non-zero exit status\n");
152 rc = parse_nvram_params(platform, process->stdout_buf,
153 process->stdout_len);
156 process_release(process);
160 static int write_nvram(struct platform_powerpc *platform)
162 struct process *process;
168 argv[1] = "--update-config";
170 argv[3] = "--partition";
174 process = process_create(platform);
175 process->path = "nvram";
176 process->argv = argv;
178 list_for_each_entry(&platform->params, param, list) {
181 if (!param->modified)
184 paramstr = talloc_asprintf(platform, "%s=%s",
185 param->name, param->value);
188 rc = process_run_sync(process);
190 talloc_free(paramstr);
192 if (rc || !process_exit_ok(process)) {
194 pb_log("nvram update process returned "
195 "non-zero exit status\n");
200 process_release(process);
204 static const char *get_param(struct platform_powerpc *platform,
209 list_for_each_entry(&platform->params, param, list)
210 if (!strcmp(param->name, name))
215 static void set_param(struct platform_powerpc *platform, const char *name,
220 list_for_each_entry(&platform->params, param, list) {
221 if (strcmp(param->name, name))
224 if (!strcmp(param->value, value))
227 talloc_free(param->value);
228 param->value = talloc_strdup(param, value);
229 param->modified = true;
234 param = talloc(platform, struct param);
235 param->modified = true;
236 param->name = talloc_strdup(platform, name);
237 param->value = talloc_strdup(platform, value);
238 list_add(&platform->params, ¶m->list);
241 static int parse_hwaddr(struct interface_config *ifconf, char *str)
245 if (strlen(str) != strlen("00:00:00:00:00:00"))
248 for (i = 0; i < HWADDR_SIZE; i++) {
252 byte[0] = str[i * 3 + 0];
253 byte[1] = str[i * 3 + 1];
256 x = strtoul(byte, &endp, 16);
257 if (endp != byte + 2)
260 ifconf->hwaddr[i] = x & 0xff;
266 static int parse_one_interface_config(struct config *config,
269 struct interface_config *ifconf;
272 ifconf = talloc_zero(config, struct interface_config);
274 if (!confstr || !strlen(confstr))
277 /* first token should be the mac address */
278 tok = strtok_r(confstr, ",", &saveptr);
282 if (parse_hwaddr(ifconf, tok))
285 /* second token is the method */
286 tok = strtok_r(NULL, ",", &saveptr);
287 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
288 ifconf->ignore = true;
290 } else if (!strcmp(tok, "dhcp")) {
291 ifconf->method = CONFIG_METHOD_DHCP;
293 } else if (!strcmp(tok, "static")) {
294 ifconf->method = CONFIG_METHOD_STATIC;
296 /* ip/mask, [optional] gateway */
297 tok = strtok_r(NULL, ",", &saveptr);
300 ifconf->static_config.address =
301 talloc_strdup(ifconf, tok);
303 tok = strtok_r(NULL, ",", &saveptr);
305 ifconf->static_config.gateway =
306 talloc_strdup(ifconf, tok);
310 pb_log("Unknown network configuration method %s\n", tok);
314 config->network.interfaces = talloc_realloc(config,
315 config->network.interfaces,
316 struct interface_config *,
317 ++config->network.n_interfaces);
319 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
327 static int parse_one_dns_config(struct config *config,
332 for (tok = strtok_r(confstr, ",", &saveptr); tok;
333 tok = strtok_r(NULL, ",", &saveptr)) {
335 char *server = talloc_strdup(config, tok);
337 config->network.dns_servers = talloc_realloc(config,
338 config->network.dns_servers, const char *,
339 ++config->network.n_dns_servers);
341 config->network.dns_servers[config->network.n_dns_servers - 1]
348 static void populate_network_config(struct platform_powerpc *platform,
349 struct config *config)
351 char *val, *saveptr = NULL;
355 cval = get_param(platform, "petitboot,network");
356 if (!cval || !strlen(cval))
359 val = talloc_strdup(config, cval);
364 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
368 if (!strncasecmp(tok, "dns,", strlen("dns,")))
369 parse_one_dns_config(config, tok + strlen("dns,"));
371 parse_one_interface_config(config, tok);
378 static void populate_bootdev_config(struct platform_powerpc *platform,
379 struct config *config)
384 config->boot_device = NULL;
386 val = get_param(platform, "petitboot,bootdev");
387 if (!val || !strlen(val))
390 if (!strncmp(val, "uuid:", strlen("uuid:"))) {
391 config->boot_device = talloc_strdup(config,
392 val + strlen("uuid:"));
394 } else if (!strncmp(val, "mac:", strlen("mac:"))) {
395 config->boot_device = talloc_strdup(config,
396 val + strlen("mac:"));
399 pb_log("bootdev config is in an unknown format "
400 "(expected uuid:... or mac:...)");
404 static void populate_config(struct platform_powerpc *platform,
405 struct config *config)
409 unsigned long timeout;
411 /* if the "auto-boot?' property is present and "false", disable auto
413 val = get_param(platform, "auto-boot?");
414 config->autoboot_enabled = !val || strcmp(val, "false");
416 val = get_param(platform, "petitboot,timeout");
418 timeout = strtoul(val, &end, 10);
420 if (timeout >= INT_MAX)
422 config->autoboot_timeout_sec = (int)timeout;
426 populate_network_config(platform, config);
428 populate_bootdev_config(platform, config);
430 if (!config->debug) {
431 val = get_param(platform, "petitboot,debug?");
432 config->debug = val && !strcmp(val, "true");
436 static char *iface_config_str(void *ctx, struct interface_config *config)
440 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
441 * different hardware address formats */
442 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
443 config->hwaddr[0], config->hwaddr[1],
444 config->hwaddr[2], config->hwaddr[3],
445 config->hwaddr[4], config->hwaddr[5]);
447 if (config->ignore) {
448 str = talloc_asprintf_append(str, "ignore");
450 } else if (config->method == CONFIG_METHOD_DHCP) {
451 str = talloc_asprintf_append(str, "dhcp");
453 } else if (config->method == CONFIG_METHOD_STATIC) {
454 str = talloc_asprintf_append(str, "static,%s%s%s",
455 config->static_config.address,
456 config->static_config.gateway ? "," : "",
457 config->static_config.gateway ?: "");
462 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
467 str = talloc_strdup(ctx, "dns,");
468 for (i = 0; i < n; i++) {
469 str = talloc_asprintf_append(str, "%s%s",
477 static void update_string_config(struct platform_powerpc *platform,
478 const char *name, const char *value)
482 cur = get_param(platform, name);
484 /* don't set an empty parameter if it doesn't already exist */
485 if (!cur && !strlen(value))
488 set_param(platform, name, value);
491 static void update_network_config(struct platform_powerpc *platform,
492 struct config *config)
497 val = talloc_strdup(platform, "");
499 for (i = 0; i < config->network.n_interfaces; i++) {
500 char *iface_str = iface_config_str(platform,
501 config->network.interfaces[i]);
502 val = talloc_asprintf_append(val, "%s%s",
503 *val == '\0' ? "" : " ", iface_str);
504 talloc_free(iface_str);
507 if (config->network.n_dns_servers) {
508 char *dns_str = dns_config_str(platform,
509 config->network.dns_servers,
510 config->network.n_dns_servers);
511 val = talloc_asprintf_append(val, "%s%s",
512 *val == '\0' ? "" : " ", dns_str);
513 talloc_free(dns_str);
516 update_string_config(platform, "petitboot,network", val);
521 static void update_bootdev_config(struct platform_powerpc *platform,
522 struct config *config)
524 char *val, *tmp = NULL;
526 if (!config->boot_device)
529 tmp = val = talloc_asprintf(platform,
530 "uuid:%s", config->boot_device);
532 update_string_config(platform, "petitboot,bootdev", val);
536 static int update_config(struct platform_powerpc *platform,
537 struct config *config, struct config *defaults)
542 if (config->autoboot_enabled == defaults->autoboot_enabled)
545 val = config->autoboot_enabled ? "true" : "false";
546 update_string_config(platform, "auto-boot?", val);
548 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
551 val = tmp = talloc_asprintf(platform, "%d",
552 config->autoboot_timeout_sec);
554 update_string_config(platform, "petitboot,timeout", val);
558 update_network_config(platform, config);
560 update_bootdev_config(platform, config);
562 return write_nvram(platform);
565 static void set_exclusive_devtype(struct config *config,
566 enum device_type devtype)
568 config->n_boot_priorities = 2;
569 config->boot_priorities = talloc_realloc(config,
570 config->boot_priorities, struct boot_priority,
571 config->n_boot_priorities);
572 config->boot_priorities[0].type = devtype;
573 config->boot_priorities[0].priority = 0;
574 config->boot_priorities[1].type = DEVICE_TYPE_ANY;
575 config->boot_priorities[1].priority = -1;
578 /* bootdev options that we recognise */
580 IPMI_BOOTDEV_NONE = 0x00,
581 IPMI_BOOTDEV_NETWORK = 0x01,
582 IPMI_BOOTDEV_DISK = 0x2,
583 IPMI_BOOTDEV_SAFE = 0x3,
584 IPMI_BOOTDEV_CDROM = 0x5,
585 IPMI_BOOTDEV_SETUP = 0x6,
588 static int read_bootdev_sysparam(const char *name, uint8_t *val)
594 strcpy(path, sysparams_dir);
595 assert(strlen(name) < sizeof(path) - strlen(path));
598 fd = open(path, O_RDONLY);
600 pb_debug("powerpc: can't access sysparam %s\n",
605 rc = read(fd, buf, sizeof(buf));
609 /* bootdev definitions should only be one byte in size */
611 pb_debug("powerpc: sysparam %s read returned %d\n",
616 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
621 case IPMI_BOOTDEV_NONE:
622 case IPMI_BOOTDEV_NETWORK:
623 case IPMI_BOOTDEV_DISK:
624 case IPMI_BOOTDEV_SAFE:
625 case IPMI_BOOTDEV_CDROM:
626 case IPMI_BOOTDEV_SETUP:
633 static int write_bootdev_sysparam(const char *name, uint8_t val)
638 strcpy(path, sysparams_dir);
639 assert(strlen(name) < sizeof(path) - strlen(path));
642 fd = open(path, O_WRONLY);
644 pb_debug("powerpc: can't access sysparam %s for writing\n",
651 rc = write(fd, &val, sizeof(val));
652 if (rc == sizeof(val)) {
657 if (rc <= 0 && errno != EINTR) {
658 pb_log("powerpc: error updating sysparam %s: %s",
659 name, strerror(errno));
668 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
673 static void parse_opal_sysparams(struct config *config)
675 uint8_t next_bootdev, default_bootdev;
676 bool next_valid, default_valid;
679 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
680 next_valid = rc == 0;
682 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
683 default_valid = rc == 0;
685 /* nothing valid? no need to change the config */
686 if (!next_valid && !default_valid)
690 /* invalidate next-boot-device setting */
691 write_bootdev_sysparam("next-boot-device", 0xff);
693 next_bootdev = default_bootdev;
696 switch (next_bootdev) {
697 case IPMI_BOOTDEV_NONE:
699 case IPMI_BOOTDEV_DISK:
700 set_exclusive_devtype(config, DEVICE_TYPE_DISK);
702 case IPMI_BOOTDEV_NETWORK:
703 set_exclusive_devtype(config, DEVICE_TYPE_NETWORK);
705 case IPMI_BOOTDEV_CDROM:
706 set_exclusive_devtype(config, DEVICE_TYPE_OPTICAL);
708 case IPMI_BOOTDEV_SETUP:
709 config->autoboot_enabled = false;
711 case IPMI_BOOTDEV_SAFE:
712 config->autoboot_enabled = false;
713 config->safe_mode = true;
718 static int load_config(struct platform *p, struct config *config)
720 struct platform_powerpc *platform = to_platform_powerpc(p);
723 rc = parse_nvram(platform);
727 populate_config(platform, config);
729 parse_opal_sysparams(config);
734 static int save_config(struct platform *p, struct config *config)
736 struct platform_powerpc *platform = to_platform_powerpc(p);
737 struct config *defaults;
740 defaults = talloc_zero(platform, struct config);
741 config_set_defaults(defaults);
743 rc = update_config(platform, config, defaults);
745 talloc_free(defaults);
749 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
751 struct platform_powerpc *platform = p->platform_data;
752 char *buf, *filename;
755 filename = talloc_asprintf(platform, "%smodel", devtree_dir);
756 rc = read_file(platform, filename, &buf, &len);
758 sysinfo->type = talloc_steal(sysinfo, buf);
759 talloc_free(filename);
761 filename = talloc_asprintf(platform, "%ssystem-id", devtree_dir);
762 rc = read_file(platform, filename, &buf, &len);
764 sysinfo->identifier = talloc_steal(sysinfo, buf);
765 talloc_free(filename);
770 static bool probe(struct platform *p, void *ctx)
772 struct platform_powerpc *platform;
776 /* we need a device tree */
777 rc = stat("/proc/device-tree", &statbuf);
781 if (!S_ISDIR(statbuf.st_mode))
784 platform = talloc(ctx, struct platform_powerpc);
785 list_init(&platform->params);
787 p->platform_data = platform;
792 static struct platform platform_powerpc = {
794 .dhcp_arch_id = 0x000e,
796 .load_config = load_config,
797 .save_config = save_config,
798 .get_sysinfo = get_sysinfo,
801 register_platform(platform_powerpc);