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