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/";
27 struct list_item list;
30 struct platform_powerpc {
34 static const char *known_params[] = {
43 #define to_platform_powerpc(p) \
44 (struct platform_powerpc *)(p->platform_data)
46 /* a partition max a max size of 64k * 16bytes = 1M */
47 static const int max_partition_size = 64 * 1024 * 16;
49 static bool param_is_known(const char *param, unsigned int len)
51 const char *known_param;
54 for (i = 0; known_params[i]; i++) {
55 known_param = known_params[i];
56 if (len == strlen(known_param) &&
57 !strncmp(param, known_param, len))
64 static int parse_nvram_params(struct platform_powerpc *platform,
67 char *pos, *name, *value;
68 unsigned int paramlen;
71 /* discard 2 header lines:
78 for (i = 0; i < len; i++) {
86 fprintf(stderr, "failure parsing nvram output\n");
90 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
95 newline = strchr(pos, '\n');
101 paramlen = strlen(pos);
104 value = strchr(pos, '=');
108 namelen = value - name;
112 if (!param_is_known(name, namelen))
117 param = talloc(platform, struct param);
118 param->modified = false;
119 param->name = talloc_strndup(platform, name, namelen);
120 param->value = talloc_strdup(platform, value);
121 list_add(&platform->params, ¶m->list);
127 static int parse_nvram(struct platform_powerpc *platform)
129 struct process *process;
134 argv[1] = "--print-config";
135 argv[2] = "--partition";
139 process = process_create(platform);
140 process->path = "nvram";
141 process->argv = argv;
142 process->keep_stdout = true;
144 rc = process_run_sync(process);
146 if (rc || !process_exit_ok(process)) {
147 fprintf(stderr, "nvram process returned "
148 "non-zero exit status\n");
151 rc = parse_nvram_params(platform, process->stdout_buf,
152 process->stdout_len);
155 process_release(process);
159 static int write_nvram(struct platform_powerpc *platform)
161 struct process *process;
167 argv[1] = "--update-config";
169 argv[3] = "--partition";
173 process = process_create(platform);
174 process->path = "nvram";
175 process->argv = argv;
177 list_for_each_entry(&platform->params, param, list) {
180 if (!param->modified)
183 paramstr = talloc_asprintf(platform, "%s=%s",
184 param->name, param->value);
187 rc = process_run_sync(process);
189 talloc_free(paramstr);
191 if (rc || !process_exit_ok(process)) {
193 pb_log("nvram update process returned "
194 "non-zero exit status\n");
199 process_release(process);
203 static const char *get_param(struct platform_powerpc *platform,
208 list_for_each_entry(&platform->params, param, list)
209 if (!strcmp(param->name, name))
214 static void set_param(struct platform_powerpc *platform, const char *name,
219 list_for_each_entry(&platform->params, param, list) {
220 if (strcmp(param->name, name))
223 if (!strcmp(param->value, value))
226 talloc_free(param->value);
227 param->value = talloc_strdup(param, value);
228 param->modified = true;
233 param = talloc(platform, struct param);
234 param->modified = true;
235 param->name = talloc_strdup(platform, name);
236 param->value = talloc_strdup(platform, value);
237 list_add(&platform->params, ¶m->list);
240 static int parse_hwaddr(struct interface_config *ifconf, char *str)
244 if (strlen(str) != strlen("00:00:00:00:00:00"))
247 for (i = 0; i < HWADDR_SIZE; i++) {
251 byte[0] = str[i * 3 + 0];
252 byte[1] = str[i * 3 + 1];
255 x = strtoul(byte, &endp, 16);
256 if (endp != byte + 2)
259 ifconf->hwaddr[i] = x & 0xff;
265 static int parse_one_interface_config(struct config *config,
268 struct interface_config *ifconf;
271 ifconf = talloc_zero(config, struct interface_config);
273 if (!confstr || !strlen(confstr))
276 /* first token should be the mac address */
277 tok = strtok_r(confstr, ",", &saveptr);
281 if (parse_hwaddr(ifconf, tok))
284 /* second token is the method */
285 tok = strtok_r(NULL, ",", &saveptr);
286 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
287 ifconf->ignore = true;
289 } else if (!strcmp(tok, "dhcp")) {
290 ifconf->method = CONFIG_METHOD_DHCP;
292 } else if (!strcmp(tok, "static")) {
293 ifconf->method = CONFIG_METHOD_STATIC;
295 /* ip/mask, [optional] gateway */
296 tok = strtok_r(NULL, ",", &saveptr);
299 ifconf->static_config.address =
300 talloc_strdup(ifconf, tok);
302 tok = strtok_r(NULL, ",", &saveptr);
304 ifconf->static_config.gateway =
305 talloc_strdup(ifconf, tok);
309 pb_log("Unknown network configuration method %s\n", tok);
313 config->network.interfaces = talloc_realloc(config,
314 config->network.interfaces,
315 struct interface_config *,
316 ++config->network.n_interfaces);
318 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
326 static int parse_one_dns_config(struct config *config,
331 for (tok = strtok_r(confstr, ",", &saveptr); tok;
332 tok = strtok_r(NULL, ",", &saveptr)) {
334 char *server = talloc_strdup(config, tok);
336 config->network.dns_servers = talloc_realloc(config,
337 config->network.dns_servers, const char *,
338 ++config->network.n_dns_servers);
340 config->network.dns_servers[config->network.n_dns_servers - 1]
347 static void populate_network_config(struct platform_powerpc *platform,
348 struct config *config)
350 char *val, *saveptr = NULL;
354 cval = get_param(platform, "petitboot,network");
355 if (!cval || !strlen(cval))
358 val = talloc_strdup(config, cval);
363 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
367 if (!strncasecmp(tok, "dns,", strlen("dns,")))
368 parse_one_dns_config(config, tok + strlen("dns,"));
370 parse_one_interface_config(config, tok);
377 static void populate_bootdev_config(struct platform_powerpc *platform,
378 struct config *config)
383 config->boot_device = NULL;
385 val = get_param(platform, "petitboot,bootdev");
386 if (!val || !strlen(val))
389 if (!strncmp(val, "uuid:", strlen("uuid:"))) {
390 config->boot_device = talloc_strdup(config,
391 val + strlen("uuid:"));
393 } else if (!strncmp(val, "mac:", strlen("mac:"))) {
394 config->boot_device = talloc_strdup(config,
395 val + strlen("mac:"));
398 pb_log("bootdev config is in an unknown format "
399 "(expected uuid:... or mac:...)");
403 static void populate_config(struct platform_powerpc *platform,
404 struct config *config)
408 unsigned long timeout;
410 /* if the "auto-boot?' property is present and "false", disable auto
412 val = get_param(platform, "auto-boot?");
413 config->autoboot_enabled = !val || strcmp(val, "false");
415 val = get_param(platform, "petitboot,timeout");
417 timeout = strtoul(val, &end, 10);
419 if (timeout >= INT_MAX)
421 config->autoboot_timeout_sec = (int)timeout;
425 populate_network_config(platform, config);
427 populate_bootdev_config(platform, config);
429 if (!config->debug) {
430 val = get_param(platform, "petitboot,debug?");
431 config->debug = val && !strcmp(val, "true");
435 static char *iface_config_str(void *ctx, struct interface_config *config)
439 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
440 * different hardware address formats */
441 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
442 config->hwaddr[0], config->hwaddr[1],
443 config->hwaddr[2], config->hwaddr[3],
444 config->hwaddr[4], config->hwaddr[5]);
446 if (config->ignore) {
447 str = talloc_asprintf_append(str, "ignore");
449 } else if (config->method == CONFIG_METHOD_DHCP) {
450 str = talloc_asprintf_append(str, "dhcp");
452 } else if (config->method == CONFIG_METHOD_STATIC) {
453 str = talloc_asprintf_append(str, "static,%s%s%s",
454 config->static_config.address,
455 config->static_config.gateway ? "," : "",
456 config->static_config.gateway ?: "");
461 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
466 str = talloc_strdup(ctx, "dns,");
467 for (i = 0; i < n; i++) {
468 str = talloc_asprintf_append(str, "%s%s",
476 static void update_string_config(struct platform_powerpc *platform,
477 const char *name, const char *value)
481 cur = get_param(platform, name);
483 /* don't set an empty parameter if it doesn't already exist */
484 if (!cur && !strlen(value))
487 set_param(platform, name, value);
490 static void update_network_config(struct platform_powerpc *platform,
491 struct config *config)
496 val = talloc_strdup(platform, "");
498 for (i = 0; i < config->network.n_interfaces; i++) {
499 char *iface_str = iface_config_str(platform,
500 config->network.interfaces[i]);
501 val = talloc_asprintf_append(val, "%s%s",
502 *val == '\0' ? "" : " ", iface_str);
503 talloc_free(iface_str);
506 if (config->network.n_dns_servers) {
507 char *dns_str = dns_config_str(platform,
508 config->network.dns_servers,
509 config->network.n_dns_servers);
510 val = talloc_asprintf_append(val, "%s%s",
511 *val == '\0' ? "" : " ", dns_str);
512 talloc_free(dns_str);
515 update_string_config(platform, "petitboot,network", val);
520 static void update_bootdev_config(struct platform_powerpc *platform,
521 struct config *config)
523 char *val, *tmp = NULL;
525 if (!config->boot_device)
528 tmp = val = talloc_asprintf(platform,
529 "uuid:%s", config->boot_device);
531 update_string_config(platform, "petitboot,bootdev", val);
535 static int update_config(struct platform_powerpc *platform,
536 struct config *config, struct config *defaults)
541 if (config->autoboot_enabled == defaults->autoboot_enabled)
544 val = config->autoboot_enabled ? "true" : "false";
545 update_string_config(platform, "auto-boot?", val);
547 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
550 val = tmp = talloc_asprintf(platform, "%d",
551 config->autoboot_timeout_sec);
553 update_string_config(platform, "petitboot,timeout", val);
557 update_network_config(platform, config);
559 update_bootdev_config(platform, config);
561 return write_nvram(platform);
564 static void set_exclusive_devtype(struct config *config,
565 enum device_type devtype)
567 config->n_boot_priorities = 2;
568 config->boot_priorities = talloc_realloc(config,
569 config->boot_priorities, struct boot_priority,
570 config->n_boot_priorities);
571 config->boot_priorities[0].type = devtype;
572 config->boot_priorities[0].priority = 0;
573 config->boot_priorities[1].type = DEVICE_TYPE_ANY;
574 config->boot_priorities[1].priority = -1;
577 /* bootdev options that we recognise */
579 IPMI_BOOTDEV_NONE = 0x00,
580 IPMI_BOOTDEV_NETWORK = 0x01,
581 IPMI_BOOTDEV_DISK = 0x2,
582 IPMI_BOOTDEV_SAFE = 0x3,
583 IPMI_BOOTDEV_CDROM = 0x5,
584 IPMI_BOOTDEV_SETUP = 0x6,
587 static int read_bootdev_sysparam(const char *name, uint8_t *val)
593 strcpy(path, sysparams_dir);
594 assert(strlen(name) < sizeof(path) - strlen(path));
597 fd = open(path, O_RDONLY);
599 pb_debug("powerpc: can't access sysparam %s\n",
604 rc = read(fd, buf, sizeof(buf));
608 /* bootdev definitions should only be one byte in size */
610 pb_debug("powerpc: sysparam %s read returned %d\n",
615 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
620 case IPMI_BOOTDEV_NONE:
621 case IPMI_BOOTDEV_NETWORK:
622 case IPMI_BOOTDEV_DISK:
623 case IPMI_BOOTDEV_SAFE:
624 case IPMI_BOOTDEV_CDROM:
625 case IPMI_BOOTDEV_SETUP:
632 static int write_bootdev_sysparam(const char *name, uint8_t val)
637 strcpy(path, sysparams_dir);
638 assert(strlen(name) < sizeof(path) - strlen(path));
641 fd = open(path, O_WRONLY);
643 pb_debug("powerpc: can't access sysparam %s for writing\n",
650 rc = write(fd, &val, sizeof(val));
651 if (rc == sizeof(val)) {
656 if (rc <= 0 && errno != EINTR) {
657 pb_log("powerpc: error updating sysparam %s: %s",
658 name, strerror(errno));
667 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
672 static void parse_opal_sysparams(struct config *config)
674 uint8_t next_bootdev, default_bootdev;
675 bool next_valid, default_valid;
678 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
679 next_valid = rc == 0;
681 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
682 default_valid = rc == 0;
684 /* nothing valid? no need to change the config */
685 if (!next_valid && !default_valid)
689 /* invalidate next-boot-device setting */
690 write_bootdev_sysparam("next-boot-device", 0xff);
692 next_bootdev = default_bootdev;
695 switch (next_bootdev) {
696 case IPMI_BOOTDEV_NONE:
698 case IPMI_BOOTDEV_DISK:
699 set_exclusive_devtype(config, DEVICE_TYPE_DISK);
701 case IPMI_BOOTDEV_NETWORK:
702 set_exclusive_devtype(config, DEVICE_TYPE_NETWORK);
704 case IPMI_BOOTDEV_CDROM:
705 set_exclusive_devtype(config, DEVICE_TYPE_OPTICAL);
707 case IPMI_BOOTDEV_SETUP:
708 config->autoboot_enabled = false;
710 case IPMI_BOOTDEV_SAFE:
711 config->autoboot_enabled = false;
712 config->safe_mode = true;
717 static int load_config(struct platform *p, struct config *config)
719 struct platform_powerpc *platform = to_platform_powerpc(p);
722 rc = parse_nvram(platform);
726 populate_config(platform, config);
728 parse_opal_sysparams(config);
733 static int save_config(struct platform *p, struct config *config)
735 struct platform_powerpc *platform = to_platform_powerpc(p);
736 struct config *defaults;
739 defaults = talloc_zero(platform, struct config);
740 config_set_defaults(defaults);
742 rc = update_config(platform, config, defaults);
744 talloc_free(defaults);
748 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
750 struct platform_powerpc *platform = p->platform_data;
754 rc = read_file(platform, "/proc/device_tree/model", &buf, &len);
756 sysinfo->type = talloc_steal(sysinfo, buf);
758 rc = read_file(platform, "/proc/device_tree/system-id", &buf, &len);
760 sysinfo->identifier = talloc_steal(sysinfo, buf);
765 static bool probe(struct platform *p, void *ctx)
767 struct platform_powerpc *platform;
771 /* we need a device tree */
772 rc = stat("/proc/device-tree", &statbuf);
776 if (!S_ISDIR(statbuf.st_mode))
779 platform = talloc(ctx, struct platform_powerpc);
780 list_init(&platform->params);
782 p->platform_data = platform;
787 static struct platform platform_powerpc = {
789 .dhcp_arch_id = 0x000e,
791 .load_config = load_config,
792 .save_config = save_config,
793 .get_sysinfo = get_sysinfo,
796 register_platform(platform_powerpc);