]> git.ozlabs.org Git - petitboot/blob - discover/platform-powerpc.c
discover: Nicely format IPMI response buffers
[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
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 "hostboot.h"
19 #include "platform.h"
20 #include "ipmi.h"
21 #include "dt.h"
22
23 static const char *partition = "common";
24 static const char *sysparams_dir = "/sys/firmware/opal/sysparams/";
25 static const char *devtree_dir = "/proc/device-tree/";
26
27 struct platform_powerpc {
28         struct param_list *params;
29         struct ipmi     *ipmi;
30         bool            ipmi_bootdev_persistent;
31         int             (*get_ipmi_bootdev)(
32                                 struct platform_powerpc *platform,
33                                 uint8_t *bootdev, bool *persistent);
34         int             (*clear_ipmi_bootdev)(
35                                 struct platform_powerpc *platform,
36                                 bool persistent);
37         int             (*set_os_boot_sensor)(
38                                 struct platform_powerpc *platform);
39         void            (*get_platform_versions)(struct system_info *info);
40 };
41
42 #define to_platform_powerpc(p) \
43         (struct platform_powerpc *)(p->platform_data)
44
45 static int parse_nvram_params(struct platform_powerpc *platform,
46                 char *buf, int len)
47 {
48         char *pos, *name, *value;
49         unsigned int paramlen;
50         int i, count;
51
52         /* discard 2 header lines:
53          * "common" partiton"
54          * ------------------
55          */
56         pos = buf;
57         count = 0;
58
59         for (i = 0; i < len; i++) {
60                 if (pos[i] == '\n')
61                         count++;
62                 if (count == 2)
63                         break;
64         }
65
66         if (i == len) {
67                 fprintf(stderr, "failure parsing nvram output\n");
68                 return -1;
69         }
70
71         for (pos = buf + i; pos < buf + len; pos += paramlen + 1) {
72                 unsigned int namelen;
73                 char *newline;
74
75                 newline = strchr(pos, '\n');
76                 if (!newline)
77                         break;
78
79                 *newline = '\0';
80
81                 paramlen = strlen(pos);
82
83                 name = pos;
84                 value = strchr(pos, '=');
85                 if (!value)
86                         continue;
87
88                 namelen = value - name;
89                 if (namelen == 0)
90                         continue;
91
92                 if (!param_list_is_known_n(platform->params, name, namelen))
93                         continue;
94
95                 *value = '\0';
96                 value++;
97
98                 param_list_set(platform->params, name, value, false);
99         }
100
101         return 0;
102 }
103
104 static int parse_nvram(struct platform_powerpc *platform)
105 {
106         struct process_stdout *stdout;
107         const char *argv[5];
108         int rc;
109
110         argv[0] = "nvram";
111         argv[1] = "--print-config";
112         argv[2] = "--partition";
113         argv[3] = partition;
114         argv[4] = NULL;
115
116         rc = process_get_stdout_argv(NULL, &stdout, argv);
117
118         if (rc) {
119                 fprintf(stderr, "nvram process returned "
120                                 "non-zero exit status\n");
121                 rc = -1;
122         } else {
123                 rc = parse_nvram_params(platform, stdout->buf, stdout->len);
124         }
125
126         talloc_free(stdout);
127         return rc;
128 }
129
130 static int write_nvram(struct platform_powerpc *platform)
131 {
132         struct process *process;
133         struct param *param;
134         const char *argv[6];
135         int rc = 0;
136
137         argv[0] = "nvram";
138         argv[1] = "--update-config";
139         argv[2] = NULL;
140         argv[3] = "--partition";
141         argv[4] = partition;
142         argv[5] = NULL;
143
144         process = process_create(platform);
145         process->path = "nvram";
146         process->argv = argv;
147
148         param_list_for_each(platform->params, param) {
149                 char *paramstr;
150
151                 if (!param->modified)
152                         continue;
153
154                 paramstr = talloc_asprintf(platform, "%s=%s",
155                                 param->name, param->value);
156                 argv[2] = paramstr;
157
158                 rc = process_run_sync(process);
159
160                 talloc_free(paramstr);
161
162                 if (rc || !process_exit_ok(process)) {
163                         rc = -1;
164                         pb_log("nvram update process returned "
165                                         "non-zero exit status\n");
166                         break;
167                 }
168         }
169
170         process_release(process);
171         return rc;
172 }
173
174 static void params_update_all(struct param_list *pl,
175         const struct config *config, const struct config *defaults)
176 {
177         char *tmp = NULL;
178         const char *val;
179
180         if (config->autoboot_enabled == defaults->autoboot_enabled)
181                 val = "";
182         else
183                 val = config->autoboot_enabled ? "true" : "false";
184
185         param_list_set_non_empty(pl, "auto-boot?", val, true);
186
187         if (config->autoboot_timeout_sec == defaults->autoboot_timeout_sec)
188                 val = "";
189         else
190                 val = tmp = talloc_asprintf(pl, "%d",
191                         config->autoboot_timeout_sec);
192
193         param_list_set_non_empty(pl, "petitboot,timeout", val, true);
194         if (tmp)
195                 talloc_free(tmp);
196
197         val = config->lang ?: "";
198         param_list_set_non_empty(pl, "petitboot,language", val, true);
199
200         if (config->allow_writes == defaults->allow_writes)
201                 val = "";
202         else
203                 val = config->allow_writes ? "true" : "false";
204         param_list_set_non_empty(pl, "petitboot,write?", val, true);
205
206         if (!config->manual_console) {
207                 val = config->boot_console ?: "";
208                 param_list_set_non_empty(pl, "petitboot,console", val, true);
209         }
210
211         val = config->http_proxy ?: "";
212         param_list_set_non_empty(pl, "petitboot,http_proxy", val, true);
213         val = config->https_proxy ?: "";
214         param_list_set_non_empty(pl, "petitboot,https_proxy", val, true);
215
216         params_update_network_values(pl, "petitboot,network", config);
217         params_update_bootdev_values(pl, "petitboot,bootdevs", config);
218 }
219
220 static void config_set_ipmi_bootdev(struct config *config, enum ipmi_bootdev bootdev,
221                 bool persistent)
222 {
223         config->ipmi_bootdev = bootdev;
224         config->ipmi_bootdev_persistent = persistent;
225
226         if (bootdev == IPMI_BOOTDEV_SAFE)
227                 config->safe_mode = true;
228 }
229
230 static int read_bootdev_sysparam(const char *name, uint8_t *val)
231 {
232         uint8_t buf[2];
233         char path[50];
234         int fd, rc;
235
236         assert(strlen(sysparams_dir) + strlen(name) < sizeof(path));
237         snprintf(path, sizeof(path), "%s%s", sysparams_dir, name);
238
239         fd = open(path, O_RDONLY);
240         if (fd < 0) {
241                 pb_debug("powerpc: can't access sysparam %s\n",
242                                 name);
243                 return -1;
244         }
245
246         rc = read(fd, buf, sizeof(buf));
247
248         close(fd);
249
250         /* bootdev definitions should only be one byte in size */
251         if (rc != 1) {
252                 pb_debug("powerpc: sysparam %s read returned %d\n",
253                                 name, rc);
254                 return -1;
255         }
256
257         pb_debug("powerpc: sysparam %s: 0x%02x\n", name, buf[0]);
258
259         if (!ipmi_bootdev_is_valid(buf[0]))
260                 return -1;
261
262         *val = buf[0];
263         return 0;
264 }
265
266 static int write_bootdev_sysparam(const char *name, uint8_t val)
267 {
268         char path[50];
269         int fd, rc;
270
271         assert(strlen(sysparams_dir) + strlen(name) < sizeof(path));
272         snprintf(path, sizeof(path), "%s%s", sysparams_dir, name);
273
274         fd = open(path, O_WRONLY);
275         if (fd < 0) {
276                 pb_debug("powerpc: can't access sysparam %s for writing\n",
277                                 name);
278                 return -1;
279         }
280
281         for (;;) {
282                 errno = 0;
283                 rc = write(fd, &val, sizeof(val));
284                 if (rc == sizeof(val)) {
285                         rc = 0;
286                         break;
287                 }
288
289                 if (rc <= 0 && errno != EINTR) {
290                         pb_log("powerpc: error updating sysparam %s: %s",
291                                         name, strerror(errno));
292                         rc = -1;
293                         break;
294                 }
295         }
296
297         close(fd);
298
299         if (!rc)
300                 pb_debug("powerpc: set sysparam %s: 0x%02x\n", name, val);
301
302         return rc;
303 }
304
305 static int clear_ipmi_bootdev_sysparams(
306                 struct platform_powerpc *platform __attribute__((unused)),
307                 bool persistent)
308 {
309         if (persistent) {
310                 /* invalidate default-boot-device setting */
311                 write_bootdev_sysparam("default-boot-device", 0xff);
312         } else {
313                 /* invalidate next-boot-device setting */
314                 write_bootdev_sysparam("next-boot-device", 0xff);
315         }
316         return 0;
317 }
318
319 static int get_ipmi_bootdev_sysparams(
320                 struct platform_powerpc *platform __attribute__((unused)),
321                 uint8_t *bootdev, bool *persistent)
322 {
323         uint8_t next_bootdev, default_bootdev;
324         bool next_valid, default_valid;
325         int rc;
326
327         rc = read_bootdev_sysparam("next-boot-device", &next_bootdev);
328         next_valid = rc == 0;
329
330         rc = read_bootdev_sysparam("default-boot-device", &default_bootdev);
331         default_valid = rc == 0;
332
333         /* nothing valid? no need to change the config */
334         if (!next_valid && !default_valid)
335                 return -1;
336
337         *persistent = !next_valid;
338         *bootdev = next_valid ? next_bootdev : default_bootdev;
339         return 0;
340 }
341
342 static int clear_ipmi_bootdev_ipmi(struct platform_powerpc *platform,
343                                    bool persistent __attribute__((unused)))
344 {
345         uint16_t resp_len;
346         uint8_t resp[1];
347         uint8_t req[] = {
348                 0x05, /* parameter selector: boot flags */
349                 0x80, /* data 1: valid */
350                 0x00, /* data 2: bootdev: no override */
351                 0x00, /* data 3: system defaults */
352                 0x00, /* data 4: no request for shared mode, mux defaults */
353                 0x00, /* data 5: no instance request */
354         };
355
356         resp_len = sizeof(resp);
357
358         ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
359                         IPMI_CMD_CHASSIS_SET_SYSTEM_BOOT_OPTIONS,
360                         req, sizeof(req),
361                         resp, &resp_len,
362                         ipmi_timeout);
363         return 0;
364 }
365
366 static int get_ipmi_bootdev_ipmi(struct platform_powerpc *platform,
367                 uint8_t *bootdev, bool *persistent)
368 {
369         uint16_t resp_len;
370         uint8_t resp[8];
371         char *debug_buf;
372         int rc;
373         uint8_t req[] = {
374                 0x05, /* parameter selector: boot flags */
375                 0x00, /* no set selector */
376                 0x00, /* no block selector */
377         };
378
379         resp_len = sizeof(resp);
380         rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
381                         IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS,
382                         req, sizeof(req),
383                         resp, &resp_len,
384                         ipmi_timeout);
385         if (rc) {
386                 pb_log("platform: error reading IPMI boot options\n");
387                 return -1;
388         }
389
390         if (resp_len != sizeof(resp)) {
391                 pb_log("platform: unexpected length (%d) in "
392                                 "boot options response\n", resp_len);
393                 return -1;
394         }
395
396         debug_buf = format_buffer(platform, resp, resp_len);
397         pb_debug_fn("IPMI get_bootdev response:\n%s\n", debug_buf);
398         talloc_free(debug_buf);
399
400         if (resp[0] != 0) {
401                 pb_log("platform: non-zero completion code %d from IPMI req\n",
402                                 resp[0]);
403                 return -1;
404         }
405
406         /* check for correct parameter version */
407         if ((resp[1] & 0xf) != 0x1) {
408                 pb_log("platform: unexpected version (0x%x) in "
409                                 "boot options response\n", resp[0]);
410                 return -1;
411         }
412
413         /* check for valid paramters */
414         if (resp[2] & 0x80) {
415                 pb_debug("platform: boot options are invalid/locked\n");
416                 return -1;
417         }
418
419         *persistent = false;
420
421         /* check for valid flags */
422         if (!(resp[3] & 0x80)) {
423                 pb_debug("platform: boot flags are invalid, ignoring\n");
424                 return -1;
425         }
426
427         *persistent = resp[3] & 0x40;
428         *bootdev = (resp[4] >> 2) & 0x0f;
429         return 0;
430 }
431
432 static int set_ipmi_os_boot_sensor(struct platform_powerpc *platform)
433 {
434         int sensor_number;
435         uint16_t resp_len;
436         uint8_t resp[1];
437         uint8_t req[] = {
438                 0x00, /* sensor number: os boot */
439                 0xA9, /* operation: set everything */
440                 0x00, /* sensor reading: none */
441                 0x40, /* assertion mask lsb: set state 6 */
442                 0x00, /* assertion mask msb: none */
443                 0x00, /* deassertion mask lsb: none */
444                 0x00, /* deassertion mask msb: none */
445                 0x00, /* event data 1: none */
446                 0x00, /* event data 2: none */
447                 0x00, /* event data 3: none */
448         };
449
450         sensor_number = get_ipmi_sensor(platform, IPMI_SENSOR_ID_OS_BOOT);
451         if (sensor_number < 0) {
452                 pb_log("Couldn't find OS boot sensor in device tree\n");
453                 return -1;
454         }
455
456         req[0] = sensor_number;
457
458         resp_len = sizeof(resp);
459
460         ipmi_transaction(platform->ipmi, IPMI_NETFN_SE,
461                         IPMI_CMD_SENSOR_SET,
462                         req, sizeof(req),
463                         resp, &resp_len,
464                         ipmi_timeout); return 0;
465
466         return 0;
467 }
468
469 static void get_ipmi_network_override(struct platform_powerpc *platform,
470                         struct config *config)
471 {
472         uint16_t min_len = 12, resp_len = 53, version;
473         const uint32_t magic_value = 0x21706221;
474         uint8_t resp[resp_len];
475         char *debug_buf;
476         uint32_t cookie;
477         bool persistent;
478         int i, rc;
479         uint8_t req[] = {
480                 0x61, /* parameter selector: OEM section (network) */
481                 0x00, /* no set selector */
482                 0x00, /* no block selector */
483         };
484
485         rc = ipmi_transaction(platform->ipmi, IPMI_NETFN_CHASSIS,
486                         IPMI_CMD_CHASSIS_GET_SYSTEM_BOOT_OPTIONS,
487                         req, sizeof(req),
488                         resp, &resp_len,
489                         ipmi_timeout);
490
491         debug_buf = format_buffer(platform, resp, resp_len);
492         pb_debug_fn("IPMI net override response:\n%s\n", debug_buf);
493         talloc_free(debug_buf);
494
495         if (rc) {
496                 pb_debug("IPMI network config option unavailable\n");
497                 return;
498         }
499
500         if (resp_len < min_len) {
501                 pb_debug("IPMI net response too small\n");
502                 return;
503         }
504
505         if (resp[0] != 0) {
506                 pb_log("platform: non-zero completion code %d from IPMI network req\n",
507                        resp[0]);
508                 return;
509         }
510
511         /* Check for correct parameter version */
512         if ((resp[1] & 0xf) != 0x1) {
513                 pb_log("platform: unexpected version (0x%x) in network override response\n",
514                        resp[0]);
515                 return;
516         }
517
518         /* Check that the parameters are valid */
519         if (resp[2] & 0x80) {
520                 pb_debug("platform: network override is invalid/locked\n");
521                 return;
522         }
523
524         /* Check for valid parameters in the boot flags section */
525         if (!(resp[3] & 0x80)) {
526                 pb_debug("platform: network override valid flag not set\n");
527                 return;
528         }
529         /* Read the persistent flag; if it is set we need to save this config */
530         persistent = resp[3] & 0x40;
531         if (persistent)
532                 pb_debug("platform: network override is persistent\n");
533
534         /* Check 4-byte cookie value */
535         i = 4;
536         memcpy(&cookie, &resp[i], sizeof(cookie));
537         cookie = __be32_to_cpu(cookie);
538         if (cookie != magic_value) {
539                 pb_log_fn("Incorrect cookie %x\n", cookie);
540                 return;
541         }
542         i += sizeof(cookie);
543
544         /* Check 2-byte version number */
545         memcpy(&version, &resp[i], sizeof(version));
546         version = __be16_to_cpu(version);
547         i += sizeof(version);
548         if (version != 1) {
549                 pb_debug("Unexpected version: %u\n", version);
550                 return;
551         }
552
553         /* Interpret the rest of the interface config */
554         rc = parse_ipmi_interface_override(config, &resp[i], resp_len - i);
555
556         if (!rc && persistent) {
557                 /* Write this new config to NVRAM */
558                 params_update_network_values(platform->params,
559                         "petitboot,network", config);
560                 rc = write_nvram(platform);
561                 if (rc)
562                         pb_log("platform: Failed to save persistent interface override\n");
563         }
564 }
565
566 static void config_get_active_consoles(struct config *config)
567 {
568         struct stat sbuf;
569         char *fsp_prop = NULL;
570
571         config->n_consoles = 2;
572         config->consoles = talloc_array(config, char *, config->n_consoles);
573         if (!config->consoles)
574                 goto err;
575
576         config->consoles[0] = talloc_asprintf(config->consoles,
577                                         "/dev/hvc0 [IPMI / Serial]");
578         config->consoles[1] = talloc_asprintf(config->consoles,
579                                         "/dev/tty1 [VGA]");
580
581         fsp_prop = talloc_asprintf(config, "%sfsps", devtree_dir);
582         if (stat(fsp_prop, &sbuf) == 0) {
583                 /* FSP based machines also have a separate serial console */
584                 config->consoles = talloc_realloc(config, config->consoles,
585                                                 char *, config->n_consoles + 1);
586                 if (!config->consoles)
587                         goto err;
588                 config->consoles[config->n_consoles++] = talloc_asprintf(
589                                                 config->consoles,
590                                                 "/dev/hvc1 [Serial]");
591         }
592
593         return;
594 err:
595         config->n_consoles = 0;
596         pb_log("Failed to allocate memory for consoles\n");
597 }
598
599 static int load_config(struct platform *p, struct config *config)
600 {
601         struct platform_powerpc *platform = to_platform_powerpc(p);
602         int rc;
603
604         rc = parse_nvram(platform);
605         if (rc)
606                 pb_log_fn("Failed to parse nvram\n");
607
608         config_populate_all(config, platform->params);
609
610         if (platform->get_ipmi_bootdev) {
611                 bool bootdev_persistent;
612                 uint8_t bootdev = IPMI_BOOTDEV_INVALID;
613                 rc = platform->get_ipmi_bootdev(platform, &bootdev,
614                                 &bootdev_persistent);
615                 if (!rc && ipmi_bootdev_is_valid(bootdev)) {
616                         config_set_ipmi_bootdev(config, bootdev,
617                                 bootdev_persistent);
618                 }
619         }
620
621         if (platform->ipmi)
622                 get_ipmi_network_override(platform, config);
623
624         config_get_active_consoles(config);
625
626         return 0;
627 }
628
629 static int save_config(struct platform *p, struct config *config)
630 {
631         struct platform_powerpc *platform = to_platform_powerpc(p);
632         struct config *defaults;
633
634         if (config->ipmi_bootdev == IPMI_BOOTDEV_INVALID &&
635             platform->clear_ipmi_bootdev) {
636                 platform->clear_ipmi_bootdev(platform,
637                                 config->ipmi_bootdev_persistent);
638                 config->ipmi_bootdev = IPMI_BOOTDEV_NONE;
639                 config->ipmi_bootdev_persistent = false;
640         }
641
642         defaults = talloc_zero(platform, struct config);
643         config_set_defaults(defaults);
644
645         params_update_all(platform->params, config, defaults);
646
647         talloc_free(defaults);
648         return write_nvram(platform);
649 }
650
651 static void pre_boot(struct platform *p, const struct config *config)
652 {
653         struct platform_powerpc *platform = to_platform_powerpc(p);
654
655         if (!config->ipmi_bootdev_persistent && platform->clear_ipmi_bootdev)
656                 platform->clear_ipmi_bootdev(platform, false);
657
658         if (platform->set_os_boot_sensor)
659                 platform->set_os_boot_sensor(platform);
660 }
661
662 static int get_sysinfo(struct platform *p, struct system_info *sysinfo)
663 {
664         struct platform_powerpc *platform = p->platform_data;
665         char *buf, *filename;
666         int len, rc;
667
668         filename = talloc_asprintf(platform, "%smodel", devtree_dir);
669         rc = read_file(platform, filename, &buf, &len);
670         if (rc == 0)
671                 sysinfo->type = talloc_steal(sysinfo, buf);
672         talloc_free(filename);
673
674         filename = talloc_asprintf(platform, "%ssystem-id", devtree_dir);
675         rc = read_file(platform, filename, &buf, &len);
676         if (rc == 0)
677                 sysinfo->identifier = talloc_steal(sysinfo, buf);
678         talloc_free(filename);
679
680         sysinfo->bmc_mac = talloc_zero_size(sysinfo, HWADDR_SIZE);
681
682         if (platform->ipmi) {
683                 ipmi_get_bmc_mac(platform->ipmi, sysinfo->bmc_mac);
684                 ipmi_get_bmc_versions(platform->ipmi, sysinfo);
685         }
686
687         if (platform->get_platform_versions)
688                 platform->get_platform_versions(sysinfo);
689
690         return 0;
691 }
692
693 static bool probe(struct platform *p, void *ctx)
694 {
695         struct platform_powerpc *platform;
696         struct stat statbuf;
697         bool bmc_present;
698         int rc;
699
700         /* we need a device tree */
701         rc = stat("/proc/device-tree", &statbuf);
702         if (rc)
703                 return false;
704
705         if (!S_ISDIR(statbuf.st_mode))
706                 return false;
707
708         platform = talloc_zero(ctx, struct platform_powerpc);
709         platform->params = talloc_zero(platform, struct param_list);
710         param_list_init(platform->params, common_known_params());
711
712         p->platform_data = platform;
713
714         bmc_present = stat("/proc/device-tree/bmc", &statbuf) == 0;
715
716         if (ipmi_present() && bmc_present) {
717                 pb_debug("platform: using direct IPMI for IPMI paramters\n");
718                 platform->ipmi = ipmi_open(platform);
719                 platform->get_ipmi_bootdev = get_ipmi_bootdev_ipmi;
720                 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_ipmi;
721                 platform->set_os_boot_sensor = set_ipmi_os_boot_sensor;
722         } else if (!stat(sysparams_dir, &statbuf)) {
723                 pb_debug("platform: using sysparams for IPMI paramters\n");
724                 platform->get_ipmi_bootdev = get_ipmi_bootdev_sysparams;
725                 platform->clear_ipmi_bootdev = clear_ipmi_bootdev_sysparams;
726
727         } else {
728                 pb_log("platform: no IPMI parameter support\n");
729         }
730
731         if (bmc_present)
732                 platform->get_platform_versions = hostboot_load_versions;
733
734         return true;
735 }
736
737
738 static struct platform platform_powerpc = {
739         .name                   = "powerpc",
740         .dhcp_arch_id           = 0x000e,
741         .probe                  = probe,
742         .load_config            = load_config,
743         .save_config            = save_config,
744         .pre_boot               = pre_boot,
745         .get_sysinfo            = get_sysinfo,
746 };
747
748 register_platform(platform_powerpc);