11 #include <talloc/talloc.h>
12 #include <list/list.h>
14 #include <process/process.h>
18 static const char *partition = "common";
19 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
25 struct list_item list;
28 struct platform_powerpc {
32 static const char *known_params[] = {
39 #define to_platform_powerpc(p) \
40 (struct platform_powerpc *)(p->platform_data)
42 /* a partition max a max size of 64k * 16bytes = 1M */
43 static const int max_partition_size = 64 * 1024 * 16;
45 static bool param_is_known(const char *param, unsigned int len)
47 const char *known_param;
50 for (i = 0; known_params[i]; i++) {
51 known_param = known_params[i];
52 if (len == strlen(known_param) &&
53 !strncmp(param, known_param, len))
60 static int parse_nvram_params(struct platform_powerpc *platform,
63 char *pos, *name, *value;
64 unsigned int paramlen;
67 /* discard 2 header lines:
74 for (i = 0; i < len; i++) {
82 fprintf(stderr, "failure parsing nvram output\n");
86 for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
91 newline = strchr(pos, '\n');
97 paramlen = strlen(pos);
100 value = strchr(pos, '=');
104 namelen = value - name;
108 if (!param_is_known(name, namelen))
113 param = talloc(platform, struct param);
114 param->modified = false;
115 param->name = talloc_strndup(platform, name, namelen);
116 param->value = talloc_strdup(platform, value);
117 list_add(&platform->params, ¶m->list);
123 static int parse_nvram(struct platform_powerpc *platform)
125 struct process *process;
130 argv[1] = "--print-config";
131 argv[2] = "--partition";
135 process = process_create(platform);
136 process->path = "nvram";
137 process->argv = argv;
138 process->keep_stdout = true;
140 rc = process_run_sync(process);
142 if (rc || !process_exit_ok(process)) {
143 fprintf(stderr, "nvram process returned "
144 "non-zero exit status\n");
147 rc = parse_nvram_params(platform, process->stdout_buf,
148 process->stdout_len);
151 process_release(process);
155 static int write_nvram(struct platform_powerpc *platform)
157 struct process *process;
163 argv[1] = "--update-config";
165 argv[3] = "--partition";
169 process = process_create(platform);
170 process->path = "nvram";
171 process->argv = argv;
173 list_for_each_entry(&platform->params, param, list) {
176 if (!param->modified)
179 paramstr = talloc_asprintf(platform, "%s=%s",
180 param->name, param->value);
183 rc = process_run_sync(process);
185 talloc_free(paramstr);
187 if (rc || !process_exit_ok(process)) {
189 pb_log("nvram update process returned "
190 "non-zero exit status\n");
195 process_release(process);
199 static const char *get_param(struct platform_powerpc *platform,
204 list_for_each_entry(&platform->params, param, list)
205 if (!strcmp(param->name, name))
210 static void set_param(struct platform_powerpc *platform, const char *name,
215 list_for_each_entry(&platform->params, param, list) {
216 if (strcmp(param->name, name))
219 if (!strcmp(param->value, value))
222 talloc_free(param->value);
223 param->value = talloc_strdup(param, value);
224 param->modified = true;
229 param = talloc(platform, struct param);
230 param->modified = true;
231 param->name = talloc_strdup(platform, name);
232 param->value = talloc_strdup(platform, value);
233 list_add(&platform->params, ¶m->list);
236 static int parse_hwaddr(struct interface_config *ifconf, char *str)
240 if (strlen(str) != strlen("00:00:00:00:00:00"))
243 for (i = 0; i < HWADDR_SIZE; i++) {
247 byte[0] = str[i * 3 + 0];
248 byte[1] = str[i * 3 + 1];
251 x = strtoul(byte, &endp, 16);
252 if (endp != byte + 2)
255 ifconf->hwaddr[i] = x & 0xff;
261 static int parse_one_interface_config(struct config *config,
264 struct interface_config *ifconf;
267 ifconf = talloc_zero(config, struct interface_config);
269 if (!confstr || !strlen(confstr))
272 /* first token should be the mac address */
273 tok = strtok_r(confstr, ",", &saveptr);
277 if (parse_hwaddr(ifconf, tok))
280 /* second token is the method */
281 tok = strtok_r(NULL, ",", &saveptr);
282 if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
283 ifconf->ignore = true;
285 } else if (!strcmp(tok, "dhcp")) {
286 ifconf->method = CONFIG_METHOD_DHCP;
288 } else if (!strcmp(tok, "static")) {
289 ifconf->method = CONFIG_METHOD_STATIC;
291 /* ip/mask, [optional] gateway */
292 tok = strtok_r(NULL, ",", &saveptr);
295 ifconf->static_config.address =
296 talloc_strdup(ifconf, tok);
298 tok = strtok_r(NULL, ",", &saveptr);
300 ifconf->static_config.gateway =
301 talloc_strdup(ifconf, tok);
305 pb_log("Unknown network configuration method %s\n", tok);
309 config->network.interfaces = talloc_realloc(config,
310 config->network.interfaces,
311 struct interface_config *,
312 ++config->network.n_interfaces);
314 config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
322 static int parse_one_dns_config(struct config *config,
327 for (tok = strtok_r(confstr, ",", &saveptr); tok;
328 tok = strtok_r(NULL, ",", &saveptr)) {
330 char *server = talloc_strdup(config, tok);
332 config->network.dns_servers = talloc_realloc(config,
333 config->network.dns_servers, const char *,
334 ++config->network.n_dns_servers);
336 config->network.dns_servers[config->network.n_dns_servers - 1]
343 static void populate_network_config(struct platform_powerpc *platform,
344 struct config *config)
350 cval = get_param(platform, "petitboot,network");
351 if (!cval || !strlen(cval))
354 val = talloc_strdup(config, cval);
359 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
363 if (!strncasecmp(tok, "dns,", strlen("dns,")))
364 parse_one_dns_config(config, tok + strlen("dns,"));
366 parse_one_interface_config(config, tok);
373 static void populate_config(struct platform_powerpc *platform,
374 struct config *config)
378 unsigned long timeout;
380 /* if the "auto-boot?' property is present and "false", disable auto
382 val = get_param(platform, "auto-boot?");
383 config->autoboot_enabled = !val || strcmp(val, "false");
385 val = get_param(platform, "petitboot,timeout");
387 timeout = strtoul(val, &end, 10);
389 if (timeout >= INT_MAX)
391 config->autoboot_timeout_sec = (int)timeout;
395 populate_network_config(platform, config);
398 static char *iface_config_str(void *ctx, struct interface_config *config)
402 /* todo: HWADDR size is hardcoded as 6, but we may need to handle
403 * different hardware address formats */
404 str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
405 config->hwaddr[0], config->hwaddr[1],
406 config->hwaddr[2], config->hwaddr[3],
407 config->hwaddr[4], config->hwaddr[5]);
409 if (config->ignore) {
410 str = talloc_asprintf_append(str, "ignore");
412 } else if (config->method == CONFIG_METHOD_DHCP) {
413 str = talloc_asprintf_append(str, "dhcp");
415 } else if (config->method == CONFIG_METHOD_STATIC) {
416 str = talloc_asprintf_append(str, "static,%s%s%s",
417 config->static_config.address,
418 config->static_config.gateway ? "," : "",
419 config->static_config.gateway ?: "");
424 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
429 str = talloc_strdup(ctx, "dns,");
430 for (i = 0; i < n; i++) {
431 str = talloc_asprintf_append(str, "%s%s",
439 static void update_string_config(struct platform_powerpc *platform,
440 const char *name, const char *value)
444 cur = get_param(platform, name);
446 /* don't set an empty parameter if it doesn't already exist */
447 if (!cur && !strlen(value))
450 set_param(platform, name, value);
453 static void update_network_config(struct platform_powerpc *platform,
454 struct config *config)
459 val = talloc_strdup(platform, "");
461 for (i = 0; i < config->network.n_interfaces; i++) {
462 char *iface_str = iface_config_str(platform,
463 config->network.interfaces[i]);
464 val = talloc_asprintf_append(val, "%s%s",
465 *val == '\0' ? "" : " ", iface_str);
466 talloc_free(iface_str);
469 if (config->network.n_dns_servers) {
470 char *dns_str = dns_config_str(platform,
471 config->network.dns_servers,
472 config->network.n_dns_servers);
473 val = talloc_asprintf_append(val, "%s%s",
474 *val == '\0' ? "" : " ", dns_str);
475 talloc_free(dns_str);
478 update_string_config(platform, "petitboot,network", val);
483 static int update_config(struct platform_powerpc *platform,
484 struct config *config, struct config *defaults)
489 if (config->autoboot_enabled == defaults->autoboot_enabled)
492 val = config->autoboot_enabled ? "true" : "false";
493 update_string_config(platform, "auto-boot?", val);
495 if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
498 val = tmp = talloc_asprintf(platform, "%d",
499 config->autoboot_timeout_sec);
501 update_string_config(platform, "petitboot,timeout", val);
505 update_network_config(platform, config);
507 return write_nvram(platform);
510 static void set_exclusive_devtype(struct config *config,
511 enum device_type devtype)
513 config->n_boot_priorities = 2;
514 config->boot_priorities = talloc_realloc(config,
515 config->boot_priorities, struct boot_priority,
516 config->n_boot_priorities);
517 config->boot_priorities[0].type = devtype;
518 config->boot_priorities[0].priority = 0;
519 config->boot_priorities[1].type = DEVICE_TYPE_ANY;
520 config->boot_priorities[1].priority = -1;
523 /* bootdev options that we recognise */
525 IPMI_BOOTDEV_NONE = 0x00,
526 IPMI_BOOTDEV_NETWORK = 0x01,
527 IPMI_BOOTDEV_DISK = 0x2,
528 IPMI_BOOTDEV_CDROM = 0x5,
529 IPMI_BOOTDEV_SETUP = 0x6,
532 static int read_bootdev_sysparam(const char *name, uint8_t *val)
538 strcpy(path, sysparams_dir);
539 assert(strlen(name) < sizeof(path) - strlen(path));
542 fd = open(path, O_RDONLY);
544 pb_debug("powerpc: can't access sysparam %s\n",
549 rc = read(fd, buf, sizeof(buf));
553 /* bootdev definitions should only be one byte in size */
555 pb_debug("powerpc: sysparam %s read returned %d\n",
560 pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
565 case IPMI_BOOTDEV_NONE:
566 case IPMI_BOOTDEV_NETWORK:
567 case IPMI_BOOTDEV_DISK:
568 case IPMI_BOOTDEV_CDROM:
569 case IPMI_BOOTDEV_SETUP:
576 static void parse_opal_sysparams(struct config *config)
578 uint8_t next_bootdev, default_bootdev;
579 bool next_valid, default_valid;
582 rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
583 next_valid = rc == 0;
585 rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
586 default_valid = rc == 0;
588 /* nothing valid? no need to change the config */
589 if (!next_valid && !default_valid)
593 next_bootdev = default_bootdev;
595 /* todo: copy default to next */
597 switch (next_bootdev) {
598 case IPMI_BOOTDEV_NONE:
600 case IPMI_BOOTDEV_DISK:
601 set_exclusive_devtype(config, DEVICE_TYPE_DISK);
603 case IPMI_BOOTDEV_NETWORK:
604 set_exclusive_devtype(config, DEVICE_TYPE_NETWORK);
606 case IPMI_BOOTDEV_CDROM:
607 set_exclusive_devtype(config, DEVICE_TYPE_OPTICAL);
609 case IPMI_BOOTDEV_SETUP:
610 config->autoboot_enabled = false;
615 static int load_config(struct platform *p, struct config *config)
617 struct platform_powerpc *platform = to_platform_powerpc(p);
620 rc = parse_nvram(platform);
624 populate_config(platform, config);
626 parse_opal_sysparams(config);
631 static int save_config(struct platform *p, struct config *config)
633 struct platform_powerpc *platform = to_platform_powerpc(p);
634 struct config *defaults;
637 defaults = talloc_zero(platform, struct config);
638 config_set_defaults(defaults);
640 rc = update_config(platform, config, defaults);
642 talloc_free(defaults);
646 static bool probe(struct platform *p, void *ctx)
648 struct platform_powerpc *platform;
652 /* we need a device tree and a working nvram binary */
653 rc = stat("/proc/device-tree", &statbuf);
657 if (!S_ISDIR(statbuf.st_mode))
660 rc = process_run_simple(ctx, "nvram", "--print-config", NULL);
661 if (!WIFEXITED(rc) || WEXITSTATUS(rc) != 0)
664 platform = talloc(ctx, struct platform_powerpc);
665 list_init(&platform->params);
667 p->platform_data = platform;
671 static struct platform platform_powerpc = {
673 .dhcp_arch_id = 0x000e,
675 .load_config = load_config,
676 .save_config = save_config,
679 register_platform(platform_powerpc);