1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Kay Sievers
15 #include "acpi-fpdt.h"
16 #include "alloc-util.h"
19 #include "time-util.h"
21 struct acpi_table_header
{
28 uint32_t oem_revision
;
29 char asl_compiler_id
[4];
30 uint32_t asl_compiler_revision
;
34 ACPI_FPDT_TYPE_BOOT
= 0,
35 ACPI_FPDT_TYPE_S3PERF
= 1,
38 struct acpi_fpdt_header
{
46 struct acpi_fpdt_boot_header
{
52 ACPI_FPDT_S3PERF_RESUME_REC
= 0,
53 ACPI_FPDT_S3PERF_SUSPEND_REC
= 1,
54 ACPI_FPDT_BOOT_REC
= 2,
57 struct acpi_fpdt_boot
{
64 uint64_t startup_start
;
65 uint64_t exit_services_entry
;
66 uint64_t exit_services_exit
;
69 int acpi_get_boot_usec(usec_t
*loader_start
, usec_t
*loader_exit
) {
70 _cleanup_free_
char *buf
= NULL
;
71 struct acpi_table_header
*tbl
;
73 struct acpi_fpdt_header
*rec
;
76 _cleanup_close_
int fd
= -1;
77 struct acpi_fpdt_boot_header hbrec
;
78 struct acpi_fpdt_boot brec
;
80 r
= read_full_file("/sys/firmware/acpi/tables/FPDT", &buf
, &l
);
84 if (l
< sizeof(struct acpi_table_header
) + sizeof(struct acpi_fpdt_header
))
87 tbl
= (struct acpi_table_header
*)buf
;
91 if (memcmp(tbl
->signature
, "FPDT", 4) != 0)
94 /* find Firmware Basic Boot Performance Pointer Record */
95 for (rec
= (struct acpi_fpdt_header
*)(buf
+ sizeof(struct acpi_table_header
));
96 (char *)rec
< buf
+ l
;
97 rec
= (struct acpi_fpdt_header
*)((char *)rec
+ rec
->length
)) {
100 if (rec
->type
!= ACPI_FPDT_TYPE_BOOT
)
102 if (rec
->length
!= sizeof(struct acpi_fpdt_header
))
112 /* read Firmware Basic Boot Performance Data Record */
113 fd
= open("/dev/mem", O_CLOEXEC
|O_RDONLY
);
117 l
= pread(fd
, &hbrec
, sizeof(struct acpi_fpdt_boot_header
), ptr
);
118 if (l
!= sizeof(struct acpi_fpdt_boot_header
))
121 if (memcmp(hbrec
.signature
, "FBPT", 4) != 0)
124 if (hbrec
.length
< sizeof(struct acpi_fpdt_boot_header
) + sizeof(struct acpi_fpdt_boot
))
127 l
= pread(fd
, &brec
, sizeof(struct acpi_fpdt_boot
), ptr
+ sizeof(struct acpi_fpdt_boot_header
));
128 if (l
!= sizeof(struct acpi_fpdt_boot
))
131 if (brec
.length
!= sizeof(struct acpi_fpdt_boot
))
134 if (brec
.type
!= ACPI_FPDT_BOOT_REC
)
137 if (brec
.exit_services_exit
== 0)
138 /* Non-UEFI compatible boot. */
141 if (brec
.startup_start
== 0 || brec
.exit_services_exit
< brec
.startup_start
)
143 if (brec
.exit_services_exit
> NSEC_PER_HOUR
)
147 *loader_start
= brec
.startup_start
/ 1000;
149 *loader_exit
= brec
.exit_services_exit
/ 1000;