ui/ncurses: Update config screen help text
[petitboot] / discover / platform-powerpc.c
1
2 #include <assert.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <limits.h>
6 #include <errno.h>
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <sys/fcntl.h>
10 #include <sys/stat.h>
11
12 #include <file/file.h>
13 #include <talloc/talloc.h>
14 #include <list/list.h>
15 #include <log/log.h>
16 #include <process/process.h>
17
18 #include "platform.h"
19 #include "ipmi.h"
20 #include "dt.h"
21
22 static const char *partition = "common";
23 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
24 static const char *devtree_dir = "/proc/device-tree/";
25 static const int ipmi_timeout = 500; /* milliseconds */
26
27 struct param {
28         char                    *name;
29         char                    *value;
30         bool                    modified;
31         struct list_item        list;
32 };
33
34 struct platform_powerpc {
35         struct list     params;
36         struct ipmi     *ipmi;
37         bool            ipmi_bootdev_persistent;
38         int             (*get_ipmi_bootdev)(
39                                 struct platform_powerpc *platform,
40                                 uint8_t *bootdev, bool *persistent);
41         int             (*clear_ipmi_bootdev)(
42                                 struct platform_powerpc *platform);
43         int             (*set_os_boot_sensor)(
44                                 struct platform_powerpc *platform);
45 };
46
47 static const char *known_params[] = {
48         "auto-boot?",
49         "petitboot,network",
50         "petitboot,timeout",
51         "petitboot,bootdev",
52         "petitboot,bootdevs",
53         "petitboot,language",
54         "petitboot,debug?",
55         NULL,
56 };
57
58 #define to_platform_powerpc(p) \
59         (struct platform_powerpc *)(p->platform_data)
60
61 /* a partition max a max size of 64k * 16bytes = 1M */
62 static const int max_partition_size = 64 * 1024 * 16;
63
64 static bool param_is_known(const char *param, unsigned int len)
65 {
66         const char *known_param;
67         unsigned int i;
68
69         for (i = 0; known_params[i]; i++) {
70                 known_param = known_params[i];
71                 if (len == strlen(known_param) &&
72                                 !strncmp(param, known_param, len))
73                         return true;
74         }
75
76         return false;
77 }
78
79 static int parse_nvram_params(struct platform_powerpc *platform,
80                 char *buf, int len)
81 {
82         char *pos, *name, *value;
83         unsigned int paramlen;
84         int i, count;
85
86         /* discard 2 header lines:
87          * "common" partiton"
88          * ------------------
89          */
90         pos = buf;
91         count = 0;
92
93         for (i = 0; i < len; i++) {
94                 if (pos[i] == '\n')
95                         count++;
96                 if (count == 2)
97                         break;
98         }
99
100         if (i == len) {
101                 fprintf(stderr, "failure parsing nvram output\n");
102                 return -1;
103         }
104
105         for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
106                 unsigned int namelen;
107                 struct param *param;
108                 char *newline;
109
110                 newline = strchr(pos, '\n');
111                 if (!newline)
112                         break;
113
114                 *newline = '\0';
115
116                 paramlen = strlen(pos);
117
118                 name = pos;
119                 value = strchr(pos, '=');
120                 if (!value)
121                         continue;
122
123                 namelen = value - name;
124                 if (namelen == 0)
125                         continue;
126
127                 if (!param_is_known(name, namelen))
128                         continue;
129
130                 value++;
131
132                 param = talloc(platform, struct param);
133                 param->modified = false;
134                 param->name = talloc_strndup(platform, name, namelen);
135                 param->value = talloc_strdup(platform, value);
136                 list_add(&platform->params, &param->list);
137         }
138
139         return 0;
140 }
141
142 static int parse_nvram(struct platform_powerpc *platform)
143 {
144         struct process *process;
145         const char *argv[5];
146         int rc;
147
148         argv[0] = "nvram";
149         argv[1] = "--print-config";
150         argv[2] = "--partition";
151         argv[3] = partition;
152         argv[4] = NULL;
153
154         process = process_create(platform);
155         process->path = "nvram";
156         process->argv = argv;
157         process->keep_stdout = true;
158
159         rc = process_run_sync(process);
160
161         if (rc || !process_exit_ok(process)) {
162                 fprintf(stderr, "nvram process returned "
163                                 "non-zero exit status\n");
164                 rc = -1;
165         } else {
166                 rc = parse_nvram_params(platform, process->stdout_buf,
167                                             process->stdout_len);
168         }
169
170         process_release(process);
171         return rc;
172 }
173
174 static int write_nvram(struct platform_powerpc *platform)
175 {
176         struct process *process;
177         struct param *param;
178         const char *argv[6];
179         int rc;
180
181         argv[0] = "nvram";
182         argv[1] = "--update-config";
183         argv[2] = NULL;
184         argv[3] = "--partition";
185         argv[4] = partition;
186         argv[5] = NULL;
187
188         process = process_create(platform);
189         process->path = "nvram";
190         process->argv = argv;
191
192         list_for_each_entry(&platform->params, param, list) {
193                 char *paramstr;
194
195                 if (!param->modified)
196                         continue;
197
198                 paramstr = talloc_asprintf(platform, "%s=%s",
199                                 param->name, param->value);
200                 argv[2] = paramstr;
201
202                 rc = process_run_sync(process);
203
204                 talloc_free(paramstr);
205
206                 if (rc || !process_exit_ok(process)) {
207                         rc = -1;
208                         pb_log("nvram update process returned "
209                                         "non-zero exit status\n");
210                         break;
211                 }
212         }
213
214         process_release(process);
215         return rc;
216 }
217
218 static const char *get_param(struct platform_powerpc *platform,
219                 const char *name)
220 {
221         struct param *param;
222
223         list_for_each_entry(&platform->params, param, list)
224                 if (!strcmp(param->name, name))
225                         return param->value;
226         return NULL;
227 }
228
229 static void set_param(struct platform_powerpc *platform, const char *name,
230                 const char *value)
231 {
232         struct param *param;
233
234         list_for_each_entry(&platform->params, param, list) {
235                 if (strcmp(param->name, name))
236                         continue;
237
238                 if (!strcmp(param->value, value))
239                         return;
240
241                 talloc_free(param->value);
242                 param->value = talloc_strdup(param, value);
243                 param->modified = true;
244                 return;
245         }
246
247
248         param = talloc(platform, struct param);
249         param->modified = true;
250         param->name = talloc_strdup(platform, name);
251         param->value = talloc_strdup(platform, value);
252         list_add(&platform->params, &param->list);
253 }
254
255 static int parse_hwaddr(struct interface_config *ifconf, char *str)
256 {
257         int i;
258
259         if (strlen(str) != strlen("00:00:00:00:00:00"))
260                 return -1;
261
262         for (i = 0; i < HWADDR_SIZE; i++) {
263                 char byte[3], *endp;
264                 unsigned long x;
265
266                 byte[0] = str[i * 3 + 0];
267                 byte[1] = str[i * 3 + 1];
268                 byte[2] = '\0';
269
270                 x = strtoul(byte, &endp, 16);
271                 if (endp != byte + 2)
272                         return -1;
273
274                 ifconf->hwaddr[i] = x & 0xff;
275         }
276
277         return 0;
278 }
279
280 static int parse_one_interface_config(struct config *config,
281                 char *confstr)
282 {
283         struct interface_config *ifconf;
284         char *tok, *saveptr;
285
286         ifconf = talloc_zero(config, struct interface_config);
287
288         if (!confstr || !strlen(confstr))
289                 goto out_err;
290
291         /* first token should be the mac address */
292         tok = strtok_r(confstr, ",", &saveptr);
293         if (!tok)
294                 goto out_err;
295
296         if (parse_hwaddr(ifconf, tok))
297                 goto out_err;
298
299         /* second token is the method */
300         tok = strtok_r(NULL, ",", &saveptr);
301         if (!tok || !strlen(tok) || !strcmp(tok, "ignore")) {
302                 ifconf->ignore = true;
303
304         } else if (!strcmp(tok, "dhcp")) {
305                 ifconf->method = CONFIG_METHOD_DHCP;
306
307         } else if (!strcmp(tok, "static")) {
308                 ifconf->method = CONFIG_METHOD_STATIC;
309
310                 /* ip/mask, [optional] gateway */
311                 tok = strtok_r(NULL, ",", &saveptr);
312                 if (!tok)
313                         goto out_err;
314                 ifconf->static_config.address =
315                         talloc_strdup(ifconf, tok);
316
317                 tok = strtok_r(NULL, ",", &saveptr);
318                 if (tok) {
319                         ifconf->static_config.gateway =
320                                 talloc_strdup(ifconf, tok);
321                 }
322
323         } else {
324                 pb_log("Unknown network configuration method %s\n", tok);
325                 goto out_err;
326         }
327
328         config->network.interfaces = talloc_realloc(config,
329                         config->network.interfaces,
330                         struct interface_config *,
331                         ++config->network.n_interfaces);
332
333         config->network.interfaces[config->network.n_interfaces - 1] = ifconf;
334
335         return 0;
336 out_err:
337         talloc_free(ifconf);
338         return -1;
339 }
340
341 static int parse_one_dns_config(struct config *config,
342                 char *confstr)
343 {
344         char *tok, *saveptr = NULL;
345
346         for (tok = strtok_r(confstr, ",", &saveptr); tok;
347                         tok = strtok_r(NULL, ",", &saveptr)) {
348
349                 char *server = talloc_strdup(config, tok);
350
351                 config->network.dns_servers = talloc_realloc(config,
352                                 config->network.dns_servers, const char *,
353                                 ++config->network.n_dns_servers);
354
355                 config->network.dns_servers[config->network.n_dns_servers - 1]
356                                 = server;
357         }
358
359         return 0;
360 }
361
362 static void populate_network_config(struct platform_powerpc *platform,
363                 struct config *config)
364 {
365         char *val, *saveptr = NULL;
366         const char *cval;
367         int i;
368
369         cval = get_param(platform, "petitboot,network");
370         if (!cval || !strlen(cval))
371                 return;
372
373         val = talloc_strdup(config, cval);
374
375         for (i = 0; ; i++) {
376                 char *tok;
377
378                 tok = strtok_r(i == 0 ? val : NULL, " ", &saveptr);
379                 if (!tok)
380                         break;
381
382                 if (!strncasecmp(tok, "dns,", strlen("dns,")))
383                         parse_one_dns_config(config, tok + strlen("dns,"));
384                 else
385                         parse_one_interface_config(config, tok);
386
387         }
388
389         talloc_free(val);
390 }
391
392 static int read_bootdev(void *ctx, char **pos, struct autoboot_option *opt)
393 {
394         char *delim = strchr(*pos, ' ');
395         int len, prefix = 0, rc = -1;
396         enum device_type type;
397
398         if (!strncmp(*pos, "uuid:", strlen("uuid:"))) {
399                 prefix = strlen("uuid:");
400                 opt->boot_type = BOOT_DEVICE_UUID;
401                 rc = 0;
402         } else if (!strncmp(*pos, "mac:", strlen("mac:"))) {
403                 prefix = strlen("mac:");
404                 opt->boot_type = BOOT_DEVICE_UUID;
405                 rc = 0;
406         } else {
407                 type = find_device_type(*pos);
408                 if (type != DEVICE_TYPE_UNKNOWN) {
409                         opt->type = type;
410                         opt->boot_type = BOOT_DEVICE_TYPE;
411                         rc = 0;
412                 }
413         }
414
415         if (opt->boot_type == BOOT_DEVICE_UUID) {
416                 if (delim)
417                         len = (int)(delim - *pos) - prefix;
418                 else
419                         len = strlen(*pos);
420
421                 opt->uuid = talloc_strndup(ctx, *pos + prefix, len);
422         }
423
424         /* Always advance pointer to next option or end */
425         if (delim)
426                 *pos = delim + 1;
427         else
428                 *pos += strlen(*pos);
429
430         return rc;
431 }
432
433 static void populate_bootdev_config(struct platform_powerpc *platform,
434                 struct config *config)
435 {
436         struct autoboot_option *opt, *new = NULL;
437         char *pos, *end, *old_dev = NULL;
438         const char delim = ' ';
439         unsigned int n_new = 0;
440         const char *val;
441         bool conflict;
442
443         /* Check for old-style bootdev */
444         val = get_param(platform, "petitboot,bootdev");
445         if (val && strlen(val)) {
446                 pos = talloc_strdup(config, val);
447                 if (!strncmp(val, "uuid:", strlen("uuid:")))
448                         old_dev = talloc_strdup(config,
449                                                 val + strlen("uuid:"));
450                 else if (!strncmp(val, "mac:", strlen("mac:")))
451                         old_dev = talloc_strdup(config,
452                                                 val + strlen("mac:"));
453         }
454
455         /* Check for ordered bootdevs */
456         val = get_param(platform, "petitboot,bootdevs");
457         if (!val || !strlen(val)) {
458                 pos = end = NULL;
459         } else {
460                 pos = talloc_strdup(config, val);
461                 end = strchr(pos, '\0');
462         }
463
464         while (pos && pos < end) {
465                 opt = talloc(config, struct autoboot_option);
466
467                 if (read_bootdev(config, &pos, opt)) {
468                         pb_log("bootdev config is in an unknown format "
469                                "(expected uuid:... or mac:...)");
470                         talloc_free(opt);
471                         if (strchr(pos, delim))
472                                 continue;
473                         return;
474                 }
475
476                 new = talloc_realloc(config, new, struct autoboot_option,
477                                      n_new + 1);
478                 new[n_new] = *opt;
479                 n_new++;
480                 talloc_free(opt);
481
482         }
483
484         if (!n_new && !old_dev) {
485                 /* If autoboot has been disabled, clear the default options */
486                 if (!config->autoboot_enabled) {
487                         talloc_free(config->autoboot_opts);
488                         config->n_autoboot_opts = 0;
489                 }
490                 return;
491         }
492
493         conflict = old_dev && (!n_new ||
494                                     new[0].boot_type == BOOT_DEVICE_TYPE ||
495                                     /* Canonical UUIDs are 36 characters long */
496                                     strncmp(new[0].uuid, old_dev, 36));
497
498         if (!conflict) {
499                 talloc_free(config->autoboot_opts);
500                 config->autoboot_opts = new;
501                 config->n_autoboot_opts = n_new;
502                 return;
503         }
504
505         /*
506          * Difference detected, defer to old format in case it has been updated
507          * recently
508          */
509         pb_debug("Old autoboot bootdev detected\n");
510         talloc_free(config->autoboot_opts);
511         config->autoboot_opts = talloc(config, struct autoboot_option);
512         config->autoboot_opts[0].boot_type = BOOT_DEVICE_UUID;
513         config->autoboot_opts[0].uuid = talloc_strdup(config, old_dev);
514         config->n_autoboot_opts = 1;
515 }
516
517 static void populate_config(struct platform_powerpc *platform,
518                 struct config *config)
519 {
520         const char *val;
521         char *end;
522         unsigned long timeout;
523
524         /* if the "auto-boot?' property is present and "false", disable auto
525          * boot */
526         val = get_param(platform, "auto-boot?");
527         config->autoboot_enabled = !val || strcmp(val, "false");
528
529         val = get_param(platform, "petitboot,timeout");
530         if (val) {
531                 timeout = strtoul(val, &end, 10);
532                 if (end != val) {
533                         if (timeout >= INT_MAX)
534                                 timeout = INT_MAX;
535                         config->autoboot_timeout_sec = (int)timeout;
536                 }
537         }
538
539         val = get_param(platform, "petitboot,language");
540         config->lang = val ? talloc_strdup(config, val) : NULL;
541
542         populate_network_config(platform, config);
543
544         populate_bootdev_config(platform, config);
545
546         if (!config->debug) {
547                 val = get_param(platform, "petitboot,debug?");
548                 config->debug = val && !strcmp(val, "true");
549         }
550 }
551
552 static char *iface_config_str(void *ctx, struct interface_config *config)
553 {
554         char *str;
555
556         /* todo: HWADDR size is hardcoded as 6, but we may need to handle
557          * different hardware address formats */
558         str = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x,",
559                         config->hwaddr[0], config->hwaddr[1],
560                         config->hwaddr[2], config->hwaddr[3],
561                         config->hwaddr[4], config->hwaddr[5]);
562
563         if (config->ignore) {
564                 str = talloc_asprintf_append(str, "ignore");
565
566         } else if (config->method == CONFIG_METHOD_DHCP) {
567                 str = talloc_asprintf_append(str, "dhcp");
568
569         } else if (config->method == CONFIG_METHOD_STATIC) {
570                 str = talloc_asprintf_append(str, "static,%s%s%s",
571                                 config->static_config.address,
572                                 config->static_config.gateway ? "," : "",
573                                 config->static_config.gateway ?: "");
574         }
575         return str;
576 }
577
578 static char *dns_config_str(void *ctx, const char **dns_servers, int n)
579 {
580         char *str;
581         int i;
582
583         str = talloc_strdup(ctx, "dns,");
584         for (i = 0; i < n; i++) {
585                 str = talloc_asprintf_append(str, "%s%s",
586                                 i == 0 ? "" : ",",
587                                 dns_servers[i]);
588         }
589
590         return str;
591 }
592
593 static void update_string_config(struct platform_powerpc *platform,
594                 const char *name, const char *value)
595 {
596         const char *cur;
597
598         cur = get_param(platform, name);
599
600         /* don't set an empty parameter if it doesn't already exist */
601         if (!cur && !strlen(value))
602                 return;
603
604         set_param(platform, name, value);
605 }
606
607 static void update_network_config(struct platform_powerpc *platform,
608         struct config *config)
609 {
610         unsigned int i;
611         char *val;
612
613         val = talloc_strdup(platform, "");
614
615         for (i = 0; i < config->network.n_interfaces; i++) {
616                 char *iface_str = iface_config_str(platform,
617                                         config->network.interfaces[i]);
618                 val = talloc_asprintf_append(val, "%s%s",
619                                 *val == '\0' ? "" : " ", iface_str);
620                 talloc_free(iface_str);
621         }
622
623         if (config->network.n_dns_servers) {
624                 char *dns_str = dns_config_str(platform,
625                                                 config->network.dns_servers,
626                                                 config->network.n_dns_servers);
627                 val = talloc_asprintf_append(val, "%s%s",
628                                 *val == '\0' ? "" : " ", dns_str);
629                 talloc_free(dns_str);
630         }
631
632         update_string_config(platform, "petitboot,network", val);
633
634         talloc_free(val);
635 }
636
637 static void update_bootdev_config(struct platform_powerpc *platform,
638                 struct config *config)
639 {
640         char *val = NULL, *boot_str = NULL, *tmp = NULL, *first = NULL;
641         struct autoboot_option *opt;
642         const char delim = ' ';
643         unsigned int i;
644
645         if (!config->n_autoboot_opts)
646                 first = val = "";
647         else if (config->autoboot_opts[0].boot_type == BOOT_DEVICE_UUID)
648                 first = talloc_asprintf(config, "uuid:%s",
649                                         config->autoboot_opts[0].uuid);
650         else
651                 first = "";
652
653         for (i = 0; i < config->n_autoboot_opts; i++) {
654                 opt = &config->autoboot_opts[i];
655                 switch (opt->boot_type) {
656                         case BOOT_DEVICE_TYPE:
657                                 boot_str = talloc_asprintf(config, "%s%c",
658                                                 device_type_name(opt->type),
659                                                 delim);
660                                 break;
661                         case BOOT_DEVICE_UUID:
662                                 boot_str = talloc_asprintf(config, "uuid:%s%c",
663                                                 opt->uuid, delim);
664                                 break;
665                         }
666                         tmp = val = talloc_asprintf_append(val, boot_str);
667         }
668
669         update_string_config(platform, "petitboot,bootdevs", val);
670         update_string_config(platform, "petitboot,bootdev", first);
671         talloc_free(tmp);
672         if (boot_str)
673                 talloc_free(boot_str);
674 }
675
676 static int update_config(struct platform_powerpc *platform,
677                 struct config *config, struct config *defaults)
678 {
679         char *tmp = NULL;
680         const char *val;
681
682         if (config->autoboot_enabled == defaults->autoboot_enabled)
683                 val = "";
684         else
685                 val = config->autoboot_enabled ? "true" : "false";
686         update_string_config(platform, "auto-boot?", val);
687
688         if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
689                 val = "";
690         else
691                 val = tmp = talloc_asprintf(platform, "%d",
692                                 config->autoboot_timeout_sec);
693
694         update_string_config(platform, "petitboot,timeout", val);
695         if (tmp)
696                 talloc_free(tmp);
697
698         val = config->lang ?: "";
699         update_string_config(platform, "petitboot,language", val);
700
701         update_network_config(platform, config);
702
703         update_bootdev_config(platform, config);
704
705         return write_nvram(platform);
706 }
707
708 static void set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
709                 bool persistent)
710 {
711         config->ipmi_bootdev = bootdev;
712         config->ipmi_bootdev_persistent = persistent;
713
714         switch (bootdev) {
715         case IPMI_BOOTDEV_NONE:
716         case IPMI_BOOTDEV_DISK:
717         case IPMI_BOOTDEV_NETWORK:
718         case IPMI_BOOTDEV_CDROM:
719                 break;
720         case IPMI_BOOTDEV_SETUP:
721                 config->autoboot_enabled = false;
722                 break;
723         case IPMI_BOOTDEV_SAFE:
724                 config->autoboot_enabled = false;
725                 config->safe_mode = true;
726                 break;
727         }
728 }
729
730 static int read_bootdev_sysparam(const char *name, uint8_t *val)
731 {
732         uint8_t buf[2];
733         char path[50];
734         int fd, rc;
735
736         strcpy(path, sysparams_dir);
737         assert(strlen(name) < sizeof(path) - strlen(path));
738         strcat(path, name);
739
740         fd = open(path, O_RDONLY);
741         if (fd < 0) {
742                 pb_debug("powerpc: can't access sysparam %s\n",
743                                 name);
744                 return -1;
745         }
746
747         rc = read(fd, buf, sizeof(buf));
748
749         close(fd);
750
751         /* bootdev definitions should only be one byte in size */
752         if (rc != 1) {
753                 pb_debug("powerpc: sysparam %s read returned %d\n",
754                                 name, rc);
755                 return -1;
756         }
757
758         pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
759
760         if (!ipmi_bootdev_is_valid(buf[0]))
761                 return -1;
762
763         *val = buf[0];
764         return 0;
765 }
766
767 static int write_bootdev_sysparam(const char *name, uint8_t val)
768 {
769         char path[50];
770         int fd, rc;
771
772         strcpy(path, sysparams_dir);
773         assert(strlen(name) < sizeof(path) - strlen(path));
774         strcat(path, name);
775
776         fd = open(path, O_WRONLY);
777         if (fd < 0) {
778                 pb_debug("powerpc: can't access sysparam %s for writing\n",
779                                 name);
780                 return -1;
781         }
782
783         for (;;) {
784                 errno = 0;
785                 rc = write(fd, &val, sizeof(val));
786                 if (rc == sizeof(val)) {
787                         rc = 0;
788                         break;
789                 }
790
791                 if (rc <= 0 && errno != EINTR) {
792                         pb_log("powerpc: error updating sysparam %s: %s",
793                                         name, strerror(errno));
794                         rc = -1;
795                         break;
796                 }
797         }
798
799         close(fd);
800
801         if (!rc)
802                 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
803
804         return rc;
805 }
806
807 static int clear_ipmi_bootdev_sysparams(
808                 struct platform_powerpc *platform __attribute__((unused)))
809 {
810         /* invalidate next-boot-device setting */
811         write_bootdev_sysparam("next-boot-device", 0xff);
812         return 0;
813 }
814
815 static int get_ipmi_bootdev_sysparams(
816                 struct platform_powerpc *platform __attribute__((unused)),
817                 uint8_t *bootdev, bool *persistent)
818 {
819         uint8_t next_bootdev, default_bootdev;
820         bool next_valid, default_valid;
821         int rc;
822
823         rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
824         next_valid = rc == 0;
825
826         rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
827         default_valid = rc == 0;
828
829         /* nothing valid? no need to change the config */
830         if (!next_valid && !default_valid)
831                 return -1;
832
833         *persistent = !next_valid;
834         *bootdev = next_valid ? next_bootdev : default_bootdev;
835         return 0;
836 }
837
838 static int clear_ipmi_bootdev_ipmi(struct platform_powerpc *platform)
839 {
840         uint16_t resp_len;
841         uint8_t resp[1];
842         uint8_t req[] = {
843                 0x05, /* parameter selector: boot flags */
844                 0x80, /* data 1: valid */
845                 0x00, /* data 2: bootdev: no override */
846                 0x00, /* data 3: system defaults */
847                 0x00, /* data 4: no request for shared mode, mux defaults */
848                 0x00, /* data 5: no instance request */
849         };
850
851         resp_len = sizeof(resp);
852
853         ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
854                         IPMI_CMD_CHASSIS_SET_SYSTEM_BOOT_OPTIONS,
855                         req, sizeof(req),
856                         resp, &resp_len,
857                         ipmi_timeout);
858         return 0;
859 }
860
861 static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform,
862                 uint8_t *bootdev, bool *persistent)
863 {
864         uint16_t resp_len;
865         uint8_t resp[8];
866         int rc;
867         uint8_t req[] = {
868                 0x05, /* parameter selector: boot flags */
869                 0x00, /* no set selector */
870                 0x00, /* no block selector */
871         };
872
873         resp_len = sizeof(resp);
874         rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
875                         IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS,
876                         req, sizeof(req),
877                         resp, &resp_len,
878                         ipmi_timeout);
879         if (rc) {
880                 pb_log("platform: error reading IPMI boot options\n");
881                 return -1;
882         }
883
884         if (resp_len != sizeof(resp)) {
885                 pb_log("platform: unexpected length (%d) in "
886                                 "boot options response\n", resp_len);
887                 return -1;
888         }
889
890         if (resp[0] != 0) {
891                 pb_log("platform: non-zero completion code %d from IPMI req\n",
892                                 resp[0]);
893                 return -1;
894         }
895
896         /* check for correct parameter version */
897         if ((resp[1] & 0xf) != 0x1) {
898                 pb_log("platform: unexpected version (0x%x) in "
899                                 "boot options response\n", resp[0]);
900                 return -1;
901         }
902
903         /* check for valid paramters */
904         if (resp[2] & 0x80) {
905                 pb_debug("platform: boot options are invalid/locked\n");
906                 return -1;
907         }
908
909         *persistent = false;
910
911         /* check for valid flags */
912         if (!(resp[3] & 0x80)) {
913                 pb_debug("platform: boot flags are invalid, ignoring\n");
914                 return 0;
915         }
916
917         *persistent = resp[3] & 0x40;
918         *bootdev = (resp[4] >> 2) & 0x0f;
919         return 0;
920 }
921
922 static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform)
923 {
924         int sensor_number;
925         uint16_t resp_len;
926         uint8_t resp[1];
927         uint8_t req[] = {
928                 0x00, /* sensor number: os boot */
929                 0xA9, /* operation: set everything */
930                 0x00, /* sensor reading: none */
931                 0x40, /* assertion mask lsb: set state 6 */
932                 0x00, /* assertion mask msb: none */
933                 0x00, /* deassertion mask lsb: none */
934                 0x00, /* deassertion mask msb: none */
935                 0x00, /* event data 1: none */
936                 0x00, /* event data 2: none */
937                 0x00, /* event data 3: none */
938         };
939
940         sensor_number = get_ipmi_sensor(platform, IPMI_SENSOR_ID_OS_BOOT);
941         if (sensor_number < 0) {
942                 pb_log("Couldn't find OS boot sensor in device tree\n");
943                 return -1;
944         }
945
946         req[0] = sensor_number;
947
948         resp_len = sizeof(resp);
949
950         ipmi_transaction(platform->ipmi, IPMI_NETFN_SE,
951                         IPMI_CMD_SENSOR_SET,
952                         req, sizeof(req),
953                         resp, &resp_len,
954                         ipmi_timeout); return 0;
955
956         return 0;
957 }
958
959 static int load_config(struct platform *p, struct config *config)
960 {
961         struct platform_powerpc *platform = to_platform_powerpc(p);
962         int rc;
963
964         rc = parse_nvram(platform);
965         if (rc)
966                 return rc;
967
968         populate_config(platform, config);
969
970         if (platform->get_ipmi_bootdev) {
971                 bool bootdev_persistent;
972                 uint8_t bootdev;
973                 rc = platform->get_ipmi_bootdev(platform, &bootdev,
974                                 &bootdev_persistent);
975                 if (!rc && ipmi_bootdev_is_valid(bootdev)) {
976                         set_ipmi_bootdev(config, bootdev, bootdev_persistent);
977                 }
978         }
979
980         return 0;
981 }
982
983 static int save_config(struct platform *p, struct config *config)
984 {
985         struct platform_powerpc *platform = to_platform_powerpc(p);
986         struct config *defaults;
987         int rc;
988
989         defaults = talloc_zero(platform, struct config);
990         config_set_defaults(defaults);
991
992         rc = update_config(platform, config, defaults);
993
994         talloc_free(defaults);
995         return rc;
996 }
997
998 static void pre_boot(struct platform *p, const struct config *config)
999 {
1000         struct platform_powerpc *platform = to_platform_powerpc(p);
1001
1002         if (!config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev)
1003                 platform->clear_ipmi_bootdev(platform);
1004
1005         if (platform->set_os_boot_sensor)
1006                 platform->set_os_boot_sensor(platform);
1007 }
1008
1009 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
1010 {
1011         struct platform_powerpc *platform = p->platform_data;
1012         char *buf, *filename;
1013         int len, rc;
1014
1015         filename = talloc_asprintf(platform, "%smodel", devtree_dir);
1016         rc = read_file(platform, filename, &buf, &len);
1017         if (rc == 0)
1018                 sysinfo->type = talloc_steal(sysinfo, buf);
1019         talloc_free(filename);
1020
1021         filename = talloc_asprintf(platform, "%ssystem-id", devtree_dir);
1022         rc = read_file(platform, filename, &buf, &len);
1023         if (rc == 0)
1024                 sysinfo->identifier = talloc_steal(sysinfo, buf);
1025         talloc_free(filename);
1026
1027         return 0;
1028 }
1029
1030 static bool probe(struct platform *p, void *ctx)
1031 {
1032         struct platform_powerpc *platform;
1033         struct stat statbuf;
1034         int rc;
1035
1036         /* we need a device tree */
1037         rc = stat("/proc/device-tree", &statbuf);
1038         if (rc)
1039                 return false;
1040
1041         if (!S_ISDIR(statbuf.st_mode))
1042                 return false;
1043
1044         platform = talloc_zero(ctx, struct platform_powerpc);
1045         list_init(&platform->params);
1046
1047         p->platform_data = platform;
1048
1049         if (ipmi_present()) {
1050                 pb_debug("platform: using direct IPMI for IPMI paramters\n");
1051                 platform->ipmi = ipmi_open(platform);
1052                 platform->get_ipmi_bootdev = get_ipmi_bootdev_ipmi;
1053                 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_ipmi;
1054                 platform->set_os_boot_sensor = set_ipmi_os_boot_sensor;
1055
1056         } else if (!stat(sysparams_dir, &statbuf)) {
1057                 pb_debug("platform: using sysparams for IPMI paramters\n");
1058                 platform->get_ipmi_bootdev = get_ipmi_bootdev_sysparams;
1059                 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_sysparams;
1060
1061         } else {
1062                 pb_log("platform: no IPMI parameter support\n");
1063         }
1064
1065         return true;
1066 }
1067
1068
1069 static struct platform platform_powerpc = {
1070         .name                   = "powerpc",
1071         .dhcp_arch_id           = 0x000e,
1072         .probe                  = probe,
1073         .load_config            = load_config,
1074         .save_config            = save_config,
1075         .pre_boot               = pre_boot,
1076         .get_sysinfo            = get_sysinfo,
1077 };
1078
1079 register_platform(platform_powerpc);