]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/acpi-fpdt.c
Update mailmap and contributor list (#7006)
[thirdparty/systemd.git] / src / shared / acpi-fpdt.c
CommitLineData
c51d84dc
KS
1/***
2 This file is part of systemd.
3
4 Copyright 2013 Kay Sievers
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
a8fbdf54 20#include <errno.h>
3ffd4af2 21#include <fcntl.h>
a8fbdf54 22#include <stddef.h>
c51d84dc
KS
23#include <stdint.h>
24#include <string.h>
25#include <unistd.h>
c51d84dc 26
3ffd4af2 27#include "acpi-fpdt.h"
cf0fbc49 28#include "alloc-util.h"
3ffd4af2
LP
29#include "fd-util.h"
30#include "fileio.h"
31#include "time-util.h"
c51d84dc
KS
32
33struct 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
45enum {
46 ACPI_FPDT_TYPE_BOOT = 0,
47 ACPI_FPDT_TYPE_S3PERF = 1,
48};
49
50struct 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
58struct acpi_fpdt_boot_header {
59 char signature[4];
60 uint32_t length;
61};
62
63enum {
64 ACPI_FPDT_S3PERF_RESUME_REC = 0,
65 ACPI_FPDT_S3PERF_SUSPEND_REC = 1,
66 ACPI_FPDT_BOOT_REC = 2,
67};
68
69struct 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
81int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) {
2c64a8d0 82 _cleanup_free_ char *buf = NULL;
c51d84dc 83 struct acpi_table_header *tbl;
39883f62 84 size_t l = 0;
c51d84dc
KS
85 struct acpi_fpdt_header *rec;
86 int r;
87 uint64_t ptr = 0;
88 _cleanup_close_ int fd = -1;
89 struct acpi_fpdt_boot_header hbrec;
90 struct acpi_fpdt_boot brec;
91
92 r = read_full_file("/sys/firmware/acpi/tables/FPDT", &buf, &l);
93 if (r < 0)
94 return r;
95
96 if (l < sizeof(struct acpi_table_header) + sizeof(struct acpi_fpdt_header))
97 return -EINVAL;
98
99 tbl = (struct acpi_table_header *)buf;
100 if (l != tbl->length)
101 return -EINVAL;
102
103 if (memcmp(tbl->signature, "FPDT", 4) != 0)
104 return -EINVAL;
105
106 /* find Firmware Basic Boot Performance Pointer Record */
107 for (rec = (struct acpi_fpdt_header *)(buf + sizeof(struct acpi_table_header));
108 (char *)rec < buf + l;
109 rec = (struct acpi_fpdt_header *)((char *)rec + rec->length)) {
f576cd20
PH
110 if (rec->length <= 0)
111 break;
c51d84dc
KS
112 if (rec->type != ACPI_FPDT_TYPE_BOOT)
113 continue;
114 if (rec->length != sizeof(struct acpi_fpdt_header))
115 continue;
116
117 ptr = rec->ptr;
118 break;
119 }
120
121 if (ptr == 0)
67a47c60 122 return -ENODATA;
c51d84dc
KS
123
124 /* read Firmware Basic Boot Performance Data Record */
125 fd = open("/dev/mem", O_CLOEXEC|O_RDONLY);
126 if (fd < 0)
127 return -errno;
128
129 l = pread(fd, &hbrec, sizeof(struct acpi_fpdt_boot_header), ptr);
130 if (l != sizeof(struct acpi_fpdt_boot_header))
131 return -EINVAL;
132
133 if (memcmp(hbrec.signature, "FBPT", 4) != 0)
134 return -EINVAL;
135
136 if (hbrec.length < sizeof(struct acpi_fpdt_boot_header) + sizeof(struct acpi_fpdt_boot))
137 return -EINVAL;
138
139 l = pread(fd, &brec, sizeof(struct acpi_fpdt_boot), ptr + sizeof(struct acpi_fpdt_boot_header));
140 if (l != sizeof(struct acpi_fpdt_boot))
141 return -EINVAL;
142
143 if (brec.length != sizeof(struct acpi_fpdt_boot))
144 return -EINVAL;
145
146 if (brec.type != ACPI_FPDT_BOOT_REC)
147 return -EINVAL;
148
67a47c60
ZJS
149 if (brec.exit_services_exit == 0)
150 /* Non-UEFI compatible boot. */
151 return -ENODATA;
152
6c798009
KS
153 if (brec.startup_start == 0 || brec.exit_services_exit < brec.startup_start)
154 return -EINVAL;
155 if (brec.exit_services_exit > NSEC_PER_HOUR)
156 return -EINVAL;
157
c51d84dc
KS
158 if (loader_start)
159 *loader_start = brec.startup_start / 1000;
160 if (loader_exit)
161 *loader_exit = brec.exit_services_exit / 1000;
162
163 return 0;
164}