Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright 2013 Kay Sievers
7 :
8 : systemd is free software; you can redistribute it and/or modify it
9 : under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation; either version 2.1 of the License, or
11 : (at your option) any later version.
12 :
13 : systemd is distributed in the hope that it will be useful, but
14 : WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 : ***/
21 :
22 : #include <stdio.h>
23 : #include <stdint.h>
24 : #include <string.h>
25 : #include <unistd.h>
26 : #include <fcntl.h>
27 :
28 : #include <util.h>
29 : #include <fileio.h>
30 : #include <time-util.h>
31 : #include <acpi-fpdt.h>
32 :
33 : struct acpi_table_header {
34 : char signature[4];
35 : uint32_t length;
36 : uint8_t revision;
37 : uint8_t checksum;
38 : char oem_id[6];
39 : char oem_table_id[8];
40 : uint32_t oem_revision;
41 : char asl_compiler_id[4];
42 : uint32_t asl_compiler_revision;
43 : };
44 :
45 : enum {
46 : ACPI_FPDT_TYPE_BOOT = 0,
47 : ACPI_FPDT_TYPE_S3PERF = 1,
48 : };
49 :
50 : struct acpi_fpdt_header {
51 : uint16_t type;
52 : uint8_t length;
53 : uint8_t revision;
54 : uint8_t reserved[4];
55 : uint64_t ptr;
56 : };
57 :
58 : struct acpi_fpdt_boot_header {
59 : char signature[4];
60 : uint32_t length;
61 : };
62 :
63 : enum {
64 : ACPI_FPDT_S3PERF_RESUME_REC = 0,
65 : ACPI_FPDT_S3PERF_SUSPEND_REC = 1,
66 : ACPI_FPDT_BOOT_REC = 2,
67 : };
68 :
69 : struct acpi_fpdt_boot {
70 : uint16_t type;
71 : uint8_t length;
72 : uint8_t revision;
73 : uint8_t reserved[4];
74 : uint64_t reset_end;
75 : uint64_t load_start;
76 : uint64_t startup_start;
77 : uint64_t exit_services_entry;
78 : uint64_t exit_services_exit;
79 : };
80 :
81 0 : int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) {
82 0 : _cleanup_free_ char *buf = NULL;
83 : struct acpi_table_header *tbl;
84 0 : size_t l = 0;
85 : struct acpi_fpdt_header *rec;
86 : int r;
87 0 : uint64_t ptr = 0;
88 0 : _cleanup_close_ int fd = -1;
89 : struct acpi_fpdt_boot_header hbrec;
90 : struct acpi_fpdt_boot brec;
91 :
92 0 : r = read_full_file("/sys/firmware/acpi/tables/FPDT", &buf, &l);
93 0 : if (r < 0)
94 0 : return r;
95 :
96 0 : if (l < sizeof(struct acpi_table_header) + sizeof(struct acpi_fpdt_header))
97 0 : return -EINVAL;
98 :
99 0 : tbl = (struct acpi_table_header *)buf;
100 0 : if (l != tbl->length)
101 0 : return -EINVAL;
102 :
103 0 : if (memcmp(tbl->signature, "FPDT", 4) != 0)
104 0 : return -EINVAL;
105 :
106 : /* find Firmware Basic Boot Performance Pointer Record */
107 0 : for (rec = (struct acpi_fpdt_header *)(buf + sizeof(struct acpi_table_header));
108 0 : (char *)rec < buf + l;
109 0 : rec = (struct acpi_fpdt_header *)((char *)rec + rec->length)) {
110 0 : if (rec->length <= 0)
111 0 : break;
112 0 : if (rec->type != ACPI_FPDT_TYPE_BOOT)
113 0 : continue;
114 0 : if (rec->length != sizeof(struct acpi_fpdt_header))
115 0 : continue;
116 :
117 0 : ptr = rec->ptr;
118 0 : break;
119 : }
120 :
121 0 : if (ptr == 0)
122 0 : return -EINVAL;
123 :
124 : /* read Firmware Basic Boot Performance Data Record */
125 0 : fd = open("/dev/mem", O_CLOEXEC|O_RDONLY);
126 0 : if (fd < 0)
127 0 : return -errno;
128 :
129 0 : l = pread(fd, &hbrec, sizeof(struct acpi_fpdt_boot_header), ptr);
130 0 : if (l != sizeof(struct acpi_fpdt_boot_header))
131 0 : return -EINVAL;
132 :
133 0 : if (memcmp(hbrec.signature, "FBPT", 4) != 0)
134 0 : return -EINVAL;
135 :
136 0 : if (hbrec.length < sizeof(struct acpi_fpdt_boot_header) + sizeof(struct acpi_fpdt_boot))
137 0 : return -EINVAL;
138 :
139 0 : l = pread(fd, &brec, sizeof(struct acpi_fpdt_boot), ptr + sizeof(struct acpi_fpdt_boot_header));
140 0 : if (l != sizeof(struct acpi_fpdt_boot))
141 0 : return -EINVAL;
142 :
143 0 : if (brec.length != sizeof(struct acpi_fpdt_boot))
144 0 : return -EINVAL;
145 :
146 0 : if (brec.type != ACPI_FPDT_BOOT_REC)
147 0 : return -EINVAL;
148 :
149 0 : if (brec.startup_start == 0 || brec.exit_services_exit < brec.startup_start)
150 0 : return -EINVAL;
151 0 : if (brec.exit_services_exit > NSEC_PER_HOUR)
152 0 : return -EINVAL;
153 :
154 0 : if (loader_start)
155 0 : *loader_start = brec.startup_start / 1000;
156 0 : if (loader_exit)
157 0 : *loader_exit = brec.exit_services_exit / 1000;
158 :
159 0 : return 0;
160 : }
|