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