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