discover/platform-powerpc: read bootdev config from IPMI boot mailbox
[petitboot] / discover / platform-powerpc.c
1
2 #include <assert.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <sys/stat.h>
10 #include <asm/byteorder.h>
11 #include <limits.h>
12
13 #include <file/file.h>
14 #include <talloc/talloc.h>
15 #include <list/list.h>
16 #include <log/log.h>
17 #include <process/process.h>
18 #include <crypt/crypt.h>
19
20 #include "hostboot.h"
21 #include "platform.h"
22 #include "ipmi.h"
23 #include "dt.h"
24
25 static const char *partition = "common";
26 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
27 static const char *devtree_dir = "/proc/device-tree/";
28
29 struct platform_powerpc {
30         struct param_list *params;
31         struct ipmi     *ipmi;
32         char            *ipmi_mailbox_original_config;
33         int             (*get_ipmi_bootdev)(
34                                 struct platform_powerpc *platform,
35                                 uint8_t *bootdev, bool *persistent);
36         int             (*clear_ipmi_bootdev)(
37                                 struct platform_powerpc *platform,
38                                 bool persistent);
39         int             (*get_ipmi_boot_mailbox)(
40                                 struct platform_powerpc *platform,
41                                 char **buf);
42         int             (*clear_ipmi_boot_mailbox)(
43                                 struct platform_powerpc *platform);
44         int             (*set_os_boot_sensor)(
45                                 struct platform_powerpc *platform);
46         void            (*get_platform_versions)(struct system_info *info);
47 };
48
49 #define to_platform_powerpc(p) \
50         (struct platform_powerpc *)(p->platform_data)
51
52 static int parse_nvram_params(struct platform_powerpc *platform,
53                 char *buf, int len)
54 {
55         char *pos, *name, *value;
56         unsigned int paramlen;
57         int i, count;
58
59         /* discard 2 header lines:
60          * "common" partiton"
61          * ------------------
62          */
63         pos = buf;
64         count = 0;
65
66         for (i = 0; i < len; i++) {
67                 if (pos[i] == '\n')
68                         count++;
69                 if (count == 2)
70                         break;
71         }
72
73         if (i == len) {
74                 fprintf(stderr, "failure parsing nvram output\n");
75                 return -1;
76         }
77
78         for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
79                 unsigned int namelen;
80                 char *newline;
81
82                 newline = strchr(pos, '\n');
83                 if (!newline)
84                         break;
85
86                 *newline = '\0';
87
88                 paramlen = strlen(pos);
89
90                 name = pos;
91                 value = strchr(pos, '=');
92                 if (!value)
93                         continue;
94
95                 namelen = value - name;
96                 if (namelen == 0)
97                         continue;
98
99                 if (!param_list_is_known_n(platform->params, name, namelen))
100                         continue;
101
102                 *value = '\0';
103                 value++;
104
105                 param_list_set(platform->params, name, value, false);
106         }
107
108         return 0;
109 }
110
111 static int parse_nvram(struct platform_powerpc *platform)
112 {
113         struct process_stdout *stdout;
114         const char *argv[5];
115         int rc;
116
117         argv[0] = "nvram";
118         argv[1] = "--print-config";
119         argv[2] = "--partition";
120         argv[3] = partition;
121         argv[4] = NULL;
122
123         rc = process_get_stdout_argv(NULL, &stdout, argv);
124
125         if (rc) {
126                 fprintf(stderr, "nvram process returned "
127                                 "non-zero exit status\n");
128                 rc = -1;
129         } else {
130                 rc = parse_nvram_params(platform, stdout->buf, stdout->len);
131         }
132
133         talloc_free(stdout);
134         return rc;
135 }
136
137 static int write_nvram(struct platform_powerpc *platform)
138 {
139         struct process *process;
140         struct param *param;
141         const char *argv[6];
142         int rc = 0;
143
144         argv[0] = "nvram";
145         argv[1] = "--update-config";
146         argv[2] = NULL;
147         argv[3] = "--partition";
148         argv[4] = partition;
149         argv[5] = NULL;
150
151         process = process_create(platform);
152         process->path = "nvram";
153         process->argv = argv;
154
155         param_list_for_each(platform->params, param) {
156                 char *paramstr;
157
158                 if (!param->modified)
159                         continue;
160
161                 paramstr = talloc_asprintf(platform, "%s=%s",
162                                 param->name, param->value);
163                 argv[2] = paramstr;
164
165                 rc = process_run_sync(process);
166
167                 talloc_free(paramstr);
168
169                 if (rc || !process_exit_ok(process)) {
170                         rc = -1;
171                         pb_log("nvram update process returned "
172                                         "non-zero exit status\n");
173                         break;
174                 }
175         }
176
177         process_release(process);
178         return rc;
179 }
180
181 static void params_update_all(struct param_list *pl,
182         const struct config *config, const struct config *defaults)
183 {
184         char *tmp = NULL;
185         const char *val;
186
187         if (config->autoboot_enabled == defaults->autoboot_enabled)
188                 val = "";
189         else
190                 val = config->autoboot_enabled ? "true" : "false";
191
192         param_list_set_non_empty(pl, "auto-boot?", val, true);
193
194         if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
195                 val = "";
196         else
197                 val = tmp = talloc_asprintf(pl, "%d",
198                         config->autoboot_timeout_sec);
199
200         param_list_set_non_empty(pl, "petitboot,timeout", val, true);
201         if (tmp)
202                 talloc_free(tmp);
203
204         val = config->lang ?: "";
205         param_list_set_non_empty(pl, "petitboot,language", val, true);
206
207         if (config->allow_writes == defaults->allow_writes)
208                 val = "";
209         else
210                 val = config->allow_writes ? "true" : "false";
211         param_list_set_non_empty(pl, "petitboot,write?", val, true);
212
213         if (!config->manual_console) {
214                 val = config->boot_console ?: "";
215                 param_list_set_non_empty(pl, "petitboot,console", val, true);
216         }
217
218         val = config->http_proxy ?: "";
219         param_list_set_non_empty(pl, "petitboot,http_proxy", val, true);
220         val = config->https_proxy ?: "";
221         param_list_set_non_empty(pl, "petitboot,https_proxy", val, true);
222
223         params_update_network_values(pl, "petitboot,network", config);
224         params_update_bootdev_values(pl, "petitboot,bootdevs", config);
225 }
226
227 static void config_set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
228                 bool persistent)
229 {
230         config->ipmi_bootdev = bootdev;
231         config->ipmi_bootdev_persistent = persistent;
232
233         if (bootdev == IPMI_BOOTDEV_SAFE)
234                 config->safe_mode = true;
235 }
236
237 static int read_bootdev_sysparam(const char *name, uint8_t *val)
238 {
239         uint8_t buf[2];
240         char path[50];
241         int fd, rc;
242
243         assert(strlen(sysparams_dir) + strlen(name) < sizeof(path));
244         snprintf(path, sizeof(path), "%s%s", sysparams_dir, name);
245
246         fd = open(path, O_RDONLY);
247         if (fd < 0) {
248                 pb_debug("powerpc: can't access sysparam %s\n",
249                                 name);
250                 return -1;
251         }
252
253         rc = read(fd, buf, sizeof(buf));
254
255         close(fd);
256
257         /* bootdev definitions should only be one byte in size */
258         if (rc != 1) {
259                 pb_debug("powerpc: sysparam %s read returned %d\n",
260                                 name, rc);
261                 return -1;
262         }
263
264         pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
265
266         if (!ipmi_bootdev_is_valid(buf[0]))
267                 return -1;
268
269         *val = buf[0];
270         return 0;
271 }
272
273 static int write_bootdev_sysparam(const char *name, uint8_t val)
274 {
275         char path[50];
276         int fd, rc;
277
278         assert(strlen(sysparams_dir) + strlen(name) < sizeof(path));
279         snprintf(path, sizeof(path), "%s%s", sysparams_dir, name);
280
281         fd = open(path, O_WRONLY);
282         if (fd < 0) {
283                 pb_debug("powerpc: can't access sysparam %s for writing\n",
284                                 name);
285                 return -1;
286         }
287
288         for (;;) {
289                 errno = 0;
290                 rc = write(fd, &val, sizeof(val));
291                 if (rc == sizeof(val)) {
292                         rc = 0;
293                         break;
294                 }
295
296                 if (rc <= 0 && errno != EINTR) {
297                         pb_log("powerpc: error updating sysparam %s: %s",
298                                         name, strerror(errno));
299                         rc = -1;
300                         break;
301                 }
302         }
303
304         close(fd);
305
306         if (!rc)
307                 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
308
309         return rc;
310 }
311
312 static int clear_ipmi_bootdev_sysparams(
313                 struct platform_powerpc *platform __attribute__((unused)),
314                 bool persistent)
315 {
316         if (persistent) {
317                 /* invalidate default-boot-device setting */
318                 write_bootdev_sysparam("default-boot-device", 0xff);
319         } else {
320                 /* invalidate next-boot-device setting */
321                 write_bootdev_sysparam("next-boot-device", 0xff);
322         }
323         return 0;
324 }
325
326 static int get_ipmi_bootdev_sysparams(
327                 struct platform_powerpc *platform __attribute__((unused)),
328                 uint8_t *bootdev, bool *persistent)
329 {
330         uint8_t next_bootdev, default_bootdev;
331         bool next_valid, default_valid;
332         int rc;
333
334         rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
335         next_valid = rc == 0;
336
337         rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
338         default_valid = rc == 0;
339
340         /* nothing valid? no need to change the config */
341         if (!next_valid && !default_valid)
342                 return -1;
343
344         *persistent = !next_valid;
345         *bootdev = next_valid ? next_bootdev : default_bootdev;
346         return 0;
347 }
348
349 static int clear_ipmi_bootdev_ipmi(struct platform_powerpc *platform,
350                                    bool persistent __attribute__((unused)))
351 {
352         uint16_t resp_len;
353         uint8_t resp[1];
354         uint8_t req[] = {
355                 0x05, /* parameter selector: boot flags */
356                 0x80, /* data 1: valid */
357                 0x00, /* data 2: bootdev: no override */
358                 0x00, /* data 3: system defaults */
359                 0x00, /* data 4: no request for shared mode, mux defaults */
360                 0x00, /* data 5: no instance request */
361         };
362
363         resp_len = sizeof(resp);
364
365         ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
366                         IPMI_CMD_CHASSIS_SET_SYSTEM_BOOT_OPTIONS,
367                         req, sizeof(req),
368                         resp, &resp_len,
369                         ipmi_timeout);
370         return 0;
371 }
372
373 static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform,
374                 uint8_t *bootdev, bool *persistent)
375 {
376         uint16_t resp_len;
377         uint8_t resp[8];
378         char *debug_buf;
379         int rc;
380         uint8_t req[] = {
381                 0x05, /* parameter selector: boot flags */
382                 0x00, /* no set selector */
383                 0x00, /* no block selector */
384         };
385
386         resp_len = sizeof(resp);
387         rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
388                         IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS,
389                         req, sizeof(req),
390                         resp, &resp_len,
391                         ipmi_timeout);
392         if (rc) {
393                 pb_log("platform: error reading IPMI boot options\n");
394                 return -1;
395         }
396
397         if (resp_len != sizeof(resp)) {
398                 pb_log("platform: unexpected length (%d) in "
399                                 "boot options response\n", resp_len);
400                 return -1;
401         }
402
403         debug_buf = format_buffer(platform, resp, resp_len);
404         pb_debug_fn("IPMI get_bootdev response:\n%s\n", debug_buf);
405         talloc_free(debug_buf);
406
407         if (resp[0] != 0) {
408                 pb_log("platform: non-zero completion code %d from IPMI req\n",
409                                 resp[0]);
410                 return -1;
411         }
412
413         /* check for correct parameter version */
414         if ((resp[1] & 0xf) != 0x1) {
415                 pb_log("platform: unexpected version (0x%x) in "
416                                 "boot options response\n", resp[0]);
417                 return -1;
418         }
419
420         /* check for valid paramters */
421         if (resp[2] & 0x80) {
422                 pb_debug("platform: boot options are invalid/locked\n");
423                 return -1;
424         }
425
426         *persistent = false;
427
428         /* check for valid flags */
429         if (!(resp[3] & 0x80)) {
430                 pb_debug("platform: boot flags are invalid, ignoring\n");
431                 return -1;
432         }
433
434         *persistent = resp[3] & 0x40;
435         *bootdev = (resp[4] >> 2) & 0x0f;
436         return 0;
437 }
438
439 static int get_ipmi_boot_mailbox_block(struct platform_powerpc *platform,
440                 char *buf, uint8_t block)
441 {
442         size_t blocksize = 16;
443         uint8_t resp[3 + 16];
444         uint16_t resp_len;
445         char *debug_buf;
446         int rc;
447         uint8_t req[] = {
448                 0x07,  /* parameter selector: boot initiator mailbox */
449                 block, /* set selector */
450                 0x00,  /* no block selector */
451         };
452
453         resp_len = sizeof(resp);
454         rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
455                         IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS,
456                         req, sizeof(req),
457                         resp, &resp_len,
458                         ipmi_timeout);
459         if (rc) {
460                 pb_log("platform: error reading IPMI boot options\n");
461                 return -1;
462         }
463
464         if (resp_len < sizeof(resp)) {
465                 if (resp_len < 3) {
466                         pb_log("platform: unexpected length (%d) in "
467                                         "boot options mailbox response\n",
468                                         resp_len);
469                         return -1;
470                 }
471
472                 if (resp_len == 4) {
473                         pb_debug_fn("block %hu empty\n", block);
474                         return 0;
475                 }
476
477                 blocksize = sizeof(resp) - 3;
478                 pb_debug_fn("Mailbox block %hu returns only %zu bytes in block\n",
479                                 block, blocksize);
480         }
481
482         debug_buf = format_buffer(platform, resp, resp_len);
483         pb_debug_fn("IPMI bootdev mailbox block %hu:\n%s\n", block, debug_buf);
484         talloc_free(debug_buf);
485
486         if (resp[0] != 0) {
487                 pb_log("platform: non-zero completion code %d from IPMI req\n",
488                                 resp[0]);
489                 return -1;
490         }
491
492         /* check for correct parameter version */
493         if ((resp[1] & 0xf) != 0x1) {
494                 pb_log("platform: unexpected version (0x%x) in "
495                                 "boot mailbox response\n", resp[0]);
496                 return -1;
497         }
498
499         /* check for valid paramters */
500         if (resp[2] & 0x80) {
501                 pb_debug("platform: boot mailbox parameters are invalid/locked\n");
502                 return -1;
503         }
504
505         memcpy(buf, &resp[3], blocksize);
506
507         return blocksize;
508 }
509
510 static int get_ipmi_boot_mailbox(struct platform_powerpc *platform,
511                 char **buf)
512 {
513         char *mailbox_buffer, *prefix;
514         const size_t blocksize = 16;
515         char block_buffer[blocksize];
516         size_t mailbox_size;
517         int content_size;
518         uint8_t i;
519         int rc;
520
521         mailbox_buffer = NULL;
522         mailbox_size = 0;
523
524         /*
525          * The BMC may hold up to 255 blocks of data but more likely the number
526          * will be closer to the minimum of 5 set by the specification and error
527          * on higher numbers.
528          */
529         for (i = 0; i < UCHAR_MAX; i++) {
530                 rc = get_ipmi_boot_mailbox_block(platform, block_buffer, i);
531                 if (rc < 3 && i == 0) {
532                         /*
533                          * Immediate failure, no blocks read or missing IANA
534                          * number.
535                          */
536                         return -1;
537                 }
538                 if (rc < 1) {
539                         /* Error or no bytes read */
540                         break;
541                 }
542
543                 if (i == 0) {
544                         /*
545                          * The first three bytes of block zero are an IANA
546                          * Enterprise ID number. Check it matches the IBM
547                          * number, '2'.
548                          */
549                         if (block_buffer[0] != 0x02 ||
550                                 block_buffer[1] != 0x00 ||
551                                 block_buffer[2] != 0x00) {
552                                 pb_log_fn("IANA number unrecognised: 0x%x:0x%x:0x%x\n",
553                                                 block_buffer[0],
554                                                 block_buffer[1],
555                                                 block_buffer[2]);
556                                 return -1;
557                         }
558                 }
559
560                 mailbox_buffer = talloc_realloc(platform, mailbox_buffer,
561                                 char, mailbox_size + rc);
562                 if (!mailbox_buffer) {
563                         pb_log_fn("Failed to allocate mailbox buffer\n");
564                         return -1;
565                 }
566                 memcpy(mailbox_buffer + mailbox_size, block_buffer, rc);
567                 mailbox_size += rc;
568         }
569
570         if (i < 5)
571                 pb_log_fn("Only %hu blocks read, spec requires at least 5.\n"
572                           "Send a bug report to your preferred BMC vendor!\n",
573                           i);
574         else
575                 pb_debug_fn("%hu blocks read (%zu bytes)\n", i, mailbox_size);
576
577         if (mailbox_size < 3 + strlen("petitboot,bootdevs="))
578                 return -1;
579
580         prefix = talloc_strndup(mailbox_buffer, mailbox_buffer + 3,
581                         strlen("petitboot,bootdevs="));
582         if (!prefix) {
583                 pb_log_fn("Couldn't check prefix\n");
584                 talloc_free(mailbox_buffer);
585                 return -1;
586         }
587
588         if (strncmp(prefix, "petitboot,bootdevs=",
589                                 strlen("petitboot,bootdevs=")) != 0 ) {
590                 /* Empty or garbage */
591                 pb_debug_fn("Buffer looks unconfigured\n");
592                 talloc_free(mailbox_buffer);
593                 *buf = NULL;
594                 return 0;
595         }
596
597         /* Don't include IANA number in buffer */
598         content_size = mailbox_size - 3 - strlen("petitboot,bootdevs=");
599         *buf = talloc_memdup(platform,
600                         mailbox_buffer + 3 + strlen("petitboot,bootdevs="),
601                         content_size + 1);
602         (*buf)[content_size] = '\0';
603
604         talloc_free(mailbox_buffer);
605         return 0;
606 }
607
608 static int clear_ipmi_boot_mailbox(struct platform_powerpc *platform)
609 {
610         uint8_t req[18] = {0}; /* req (2) + blocksize (16) */
611         uint16_t resp_len;
612         uint8_t resp[1];
613         uint8_t i;
614         int rc;
615
616         req[0] = 0x07;  /* parameter selector: boot initiator mailbox */
617
618         resp_len = sizeof(resp);
619
620         for (i = 0; i < UCHAR_MAX; i++) {
621                 req[1] = i; /* set selector */
622                 rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
623                                 IPMI_CMD_CHASSIS_SET_SYSTEM_BOOT_OPTIONS,
624                                 req, sizeof(req),
625                                 resp, &resp_len,
626                                 ipmi_timeout);
627
628                 if (rc || resp[0]) {
629                         if (i == 0) {
630                                 pb_log_fn("error clearing IPMI boot mailbox, "
631                                                 "rc %d resp[0] %hu\n",
632                                                 rc, resp[0]);
633                                 return -1;
634                         }
635                         break;
636                 }
637         }
638
639         pb_debug_fn("Cleared %hu blocks\n", i);
640
641         return 0;
642 }
643
644 static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform)
645 {
646         int sensor_number;
647         uint16_t resp_len;
648         uint8_t resp[1];
649         uint8_t req[] = {
650                 0x00, /* sensor number: os boot */
651                 0xA9, /* operation: set everything */
652                 0x00, /* sensor reading: none */
653                 0x40, /* assertion mask lsb: set state 6 */
654                 0x00, /* assertion mask msb: none */
655                 0x00, /* deassertion mask lsb: none */
656                 0x00, /* deassertion mask msb: none */
657                 0x00, /* event data 1: none */
658                 0x00, /* event data 2: none */
659                 0x00, /* event data 3: none */
660         };
661
662         sensor_number = get_ipmi_sensor(platform, IPMI_SENSOR_ID_OS_BOOT);
663         if (sensor_number < 0) {
664                 pb_log("Couldn't find OS boot sensor in device tree\n");
665                 return -1;
666         }
667
668         req[0] = sensor_number;
669
670         resp_len = sizeof(resp);
671
672         ipmi_transaction(platform->ipmi, IPMI_NETFN_SE,
673                         IPMI_CMD_SENSOR_SET,
674                         req, sizeof(req),
675                         resp, &resp_len,
676                         ipmi_timeout); return 0;
677
678         return 0;
679 }
680
681 static void get_ipmi_network_override(struct platform_powerpc *platform,
682                         struct config *config)
683 {
684         uint16_t min_len = 12, resp_len = 53, version;
685         const uint32_t magic_value = 0x21706221;
686         uint8_t resp[resp_len];
687         char *debug_buf;
688         uint32_t cookie;
689         bool persistent;
690         int i, rc;
691         uint8_t req[] = {
692                 0x61, /* parameter selector: OEM section (network) */
693                 0x00, /* no set selector */
694                 0x00, /* no block selector */
695         };
696
697         rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
698                         IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS,
699                         req, sizeof(req),
700                         resp, &resp_len,
701                         ipmi_timeout);
702
703         debug_buf = format_buffer(platform, resp, resp_len);
704         pb_debug_fn("IPMI net override response:\n%s\n", debug_buf);
705         talloc_free(debug_buf);
706
707         if (rc) {
708                 pb_debug("IPMI network config option unavailable\n");
709                 return;
710         }
711
712         if (resp_len < min_len) {
713                 pb_debug("IPMI net response too small\n");
714                 return;
715         }
716
717         if (resp[0] != 0) {
718                 pb_log("platform: non-zero completion code %d from IPMI network req\n",
719                        resp[0]);
720                 return;
721         }
722
723         /* Check for correct parameter version */
724         if ((resp[1] & 0xf) != 0x1) {
725                 pb_log("platform: unexpected version (0x%x) in network override response\n",
726                        resp[0]);
727                 return;
728         }
729
730         /* Check that the parameters are valid */
731         if (resp[2] & 0x80) {
732                 pb_debug("platform: network override is invalid/locked\n");
733                 return;
734         }
735
736         /* Check for valid parameters in the boot flags section */
737         if (!(resp[3] & 0x80)) {
738                 pb_debug("platform: network override valid flag not set\n");
739                 return;
740         }
741         /* Read the persistent flag; if it is set we need to save this config */
742         persistent = resp[3] & 0x40;
743         if (persistent)
744                 pb_debug("platform: network override is persistent\n");
745
746         /* Check 4-byte cookie value */
747         i = 4;
748         memcpy(&cookie, &resp[i], sizeof(cookie));
749         cookie = __be32_to_cpu(cookie);
750         if (cookie != magic_value) {
751                 pb_log_fn("Incorrect cookie %x\n", cookie);
752                 return;
753         }
754         i += sizeof(cookie);
755
756         /* Check 2-byte version number */
757         memcpy(&version, &resp[i], sizeof(version));
758         version = __be16_to_cpu(version);
759         i += sizeof(version);
760         if (version != 1) {
761                 pb_debug("Unexpected version: %u\n", version);
762                 return;
763         }
764
765         /* Interpret the rest of the interface config */
766         rc = parse_ipmi_interface_override(config, &resp[i], resp_len - i);
767
768         if (!rc && persistent) {
769                 /* Write this new config to NVRAM */
770                 params_update_network_values(platform->params,
771                         "petitboot,network", config);
772                 rc = write_nvram(platform);
773                 if (rc)
774                         pb_log("platform: Failed to save persistent interface override\n");
775         }
776 }
777
778 static void config_get_active_consoles(struct config *config)
779 {
780         struct stat sbuf;
781         char *fsp_prop = NULL;
782
783         config->n_consoles = 2;
784         config->consoles = talloc_array(config, char *, config->n_consoles);
785         if (!config->consoles)
786                 goto err;
787
788         config->consoles[0] = talloc_asprintf(config->consoles,
789                                         "/dev/hvc0 [IPMI / Serial]");
790         config->consoles[1] = talloc_asprintf(config->consoles,
791                                         "/dev/tty1 [VGA]");
792
793         fsp_prop = talloc_asprintf(config, "%sfsps", devtree_dir);
794         if (stat(fsp_prop, &sbuf) == 0) {
795                 /* FSP based machines also have a separate serial console */
796                 config->consoles = talloc_realloc(config, config->consoles,
797                                                 char *, config->n_consoles + 1);
798                 if (!config->consoles)
799                         goto err;
800                 config->consoles[config->n_consoles++] = talloc_asprintf(
801                                                 config->consoles,
802                                                 "/dev/hvc1 [Serial]");
803         }
804
805         return;
806 err:
807         config->n_consoles = 0;
808         pb_log("Failed to allocate memory for consoles\n");
809 }
810
811 static int load_config(struct platform *p, struct config *config)
812 {
813         struct platform_powerpc *platform = to_platform_powerpc(p);
814         const char *hash;
815         int rc;
816
817         rc = parse_nvram(platform);
818         if (rc)
819                 pb_log_fn("Failed to parse nvram\n");
820
821         /*
822          * If we have an IPMI mailbox configuration available use it instead of
823          * the boot order found in NVRAM.
824          */
825         if (platform->get_ipmi_boot_mailbox) {
826                 char *mailbox;
827                 struct param *param;
828                 rc = platform->get_ipmi_boot_mailbox(platform, &mailbox);
829                 if (!rc && mailbox) {
830                         platform->ipmi_mailbox_original_config =
831                                 talloc_strdup(
832                                         platform,
833                                         param_list_get_value(
834                                                 platform->params, "petitboot,bootdevs"));
835                         param_list_set(platform->params, "petitboot,bootdevs",
836                                         mailbox, false);
837                         param = param_list_get_param(platform->params,
838                                         "petitboot,bootdevs");
839                         /* Avoid writing this to NVRAM */
840                         param->modified = false;
841                         config->ipmi_bootdev_mailbox = true;
842                         talloc_free(mailbox);
843                 }
844         }
845
846         config_populate_all(config, platform->params);
847
848         if (platform->get_ipmi_bootdev) {
849                 bool bootdev_persistent;
850                 uint8_t bootdev = IPMI_BOOTDEV_INVALID;
851                 rc = platform->get_ipmi_bootdev(platform, &bootdev,
852                                 &bootdev_persistent);
853                 if (!rc && ipmi_bootdev_is_valid(bootdev)) {
854                         config_set_ipmi_bootdev(config, bootdev,
855                                 bootdev_persistent);
856                 }
857         }
858
859         if (platform->ipmi)
860                 get_ipmi_network_override(platform, config);
861
862         config_get_active_consoles(config);
863
864
865         hash = param_list_get_value(platform->params, "petitboot,password");
866         if (hash) {
867                 rc = crypt_set_password_hash(platform, hash);
868                 if (rc)
869                         pb_log("Failed to set password hash\n");
870         }
871
872         return 0;
873 }
874
875 static int save_config(struct platform *p, struct config *config)
876 {
877         struct platform_powerpc *platform = to_platform_powerpc(p);
878         struct config *defaults;
879         struct param *param;
880
881         if (config->ipmi_bootdev == IPMI_BOOTDEV_INVALID &&
882             platform->clear_ipmi_bootdev) {
883                 platform->clear_ipmi_bootdev(platform,
884                                 config->ipmi_bootdev_persistent);
885                 config->ipmi_bootdev = IPMI_BOOTDEV_NONE;
886                 config->ipmi_bootdev_persistent = false;
887         }
888
889         if (!config->ipmi_bootdev_mailbox &&
890                         platform->ipmi_mailbox_original_config) {
891                 param = param_list_get_param(platform->params,
892                                 "petitboot,bootdevs");
893                 /* Restore old boot order if unmodified */
894                 if (!param->modified) {
895                         param_list_set(platform->params, "petitboot,bootdevs",
896                                         platform->ipmi_mailbox_original_config,
897                                         false);
898                         param->modified = false;
899                         config_populate_bootdev(config, platform->params);
900                 }
901                 platform->clear_ipmi_boot_mailbox(platform);
902                 talloc_free(platform->ipmi_mailbox_original_config);
903                 platform->ipmi_mailbox_original_config = NULL;
904         }
905
906         defaults = talloc_zero(platform, struct config);
907         config_set_defaults(defaults);
908
909         params_update_all(platform->params, config, defaults);
910
911         talloc_free(defaults);
912         return write_nvram(platform);
913 }
914
915 static void pre_boot(struct platform *p, const struct config *config)
916 {
917         struct platform_powerpc *platform = to_platform_powerpc(p);
918
919         if (!config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev)
920                 platform->clear_ipmi_bootdev(platform, false);
921
922         if (platform->set_os_boot_sensor)
923                 platform->set_os_boot_sensor(platform);
924 }
925
926 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
927 {
928         struct platform_powerpc *platform = p->platform_data;
929         char *buf, *filename;
930         int len, rc;
931
932         filename = talloc_asprintf(platform, "%smodel", devtree_dir);
933         rc = read_file(platform, filename, &buf, &len);
934         if (rc == 0)
935                 sysinfo->type = talloc_steal(sysinfo, buf);
936         talloc_free(filename);
937
938         filename = talloc_asprintf(platform, "%ssystem-id", devtree_dir);
939         rc = read_file(platform, filename, &buf, &len);
940         if (rc == 0)
941                 sysinfo->identifier = talloc_steal(sysinfo, buf);
942         talloc_free(filename);
943
944         sysinfo->bmc_mac = talloc_zero_size(sysinfo, HWADDR_SIZE);
945
946         if (platform->ipmi) {
947                 ipmi_get_bmc_mac(platform->ipmi, sysinfo->bmc_mac);
948                 ipmi_get_bmc_versions(platform->ipmi, sysinfo);
949         }
950
951         if (platform->get_platform_versions)
952                 platform->get_platform_versions(sysinfo);
953
954         return 0;
955 }
956
957 static bool restrict_clients(struct platform *p)
958 {
959         struct platform_powerpc *platform = to_platform_powerpc(p);
960
961         return param_list_get_value(platform->params, "petitboot,password") != NULL;
962 }
963
964 static int set_password(struct platform *p, const char *hash)
965 {
966         struct platform_powerpc *platform = to_platform_powerpc(p);
967
968         param_list_set(platform->params, "petitboot,password", hash, true);
969         write_nvram(platform);
970
971         return 0;
972 }
973
974 static bool probe(struct platform *p, void *ctx)
975 {
976         struct platform_powerpc *platform;
977         struct stat statbuf;
978         bool bmc_present;
979         int rc;
980
981         /* we need a device tree */
982         rc = stat("/proc/device-tree", &statbuf);
983         if (rc)
984                 return false;
985
986         if (!S_ISDIR(statbuf.st_mode))
987                 return false;
988
989         platform = talloc_zero(ctx, struct platform_powerpc);
990         platform->params = talloc_zero(platform, struct param_list);
991         param_list_init(platform->params, common_known_params());
992
993         p->platform_data = platform;
994
995         bmc_present = stat("/proc/device-tree/bmc", &statbuf) == 0;
996
997         if (ipmi_present() && bmc_present) {
998                 pb_debug("platform: using direct IPMI for IPMI paramters\n");
999                 platform->ipmi = ipmi_open(platform);
1000                 platform->get_ipmi_bootdev = get_ipmi_bootdev_ipmi;
1001                 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_ipmi;
1002                 platform->get_ipmi_boot_mailbox = get_ipmi_boot_mailbox;
1003                 platform->clear_ipmi_boot_mailbox = clear_ipmi_boot_mailbox;
1004                 platform->set_os_boot_sensor = set_ipmi_os_boot_sensor;
1005         } else if (!stat(sysparams_dir, &statbuf)) {
1006                 pb_debug("platform: using sysparams for IPMI paramters\n");
1007                 platform->get_ipmi_bootdev = get_ipmi_bootdev_sysparams;
1008                 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_sysparams;
1009
1010         } else {
1011                 pb_log("platform: no IPMI parameter support\n");
1012         }
1013
1014         if (bmc_present)
1015                 platform->get_platform_versions = hostboot_load_versions;
1016
1017         return true;
1018 }
1019
1020
1021 static struct platform platform_powerpc = {
1022         .name                   = "powerpc",
1023         .dhcp_arch_id           = 0x000e,
1024         .probe                  = probe,
1025         .load_config            = load_config,
1026         .save_config            = save_config,
1027         .pre_boot               = pre_boot,
1028         .get_sysinfo            = get_sysinfo,
1029         .restrict_clients       = restrict_clients,
1030         .set_password           = set_password,
1031 };
1032
1033 register_platform(platform_powerpc);