1 /* SPDX-License-Identifier: LGPL-2.1+ */
10 #include "acpi-fpdt.h"
11 #include "alloc-util.h"
14 #include "time-util.h"
16 struct acpi_table_header
{
23 uint32_t oem_revision
;
24 char asl_compiler_id
[4];
25 uint32_t asl_compiler_revision
;
29 ACPI_FPDT_TYPE_BOOT
= 0,
30 ACPI_FPDT_TYPE_S3PERF
= 1,
33 struct acpi_fpdt_header
{
41 struct acpi_fpdt_boot_header
{
47 ACPI_FPDT_S3PERF_RESUME_REC
= 0,
48 ACPI_FPDT_S3PERF_SUSPEND_REC
= 1,
49 ACPI_FPDT_BOOT_REC
= 2,
52 struct acpi_fpdt_boot
{
59 uint64_t startup_start
;
60 uint64_t exit_services_entry
;
61 uint64_t exit_services_exit
;
64 int acpi_get_boot_usec(usec_t
*loader_start
, usec_t
*loader_exit
) {
65 _cleanup_free_
char *buf
= NULL
;
66 struct acpi_table_header
*tbl
;
68 struct acpi_fpdt_header
*rec
;
71 _cleanup_close_
int fd
= -1;
72 struct acpi_fpdt_boot_header hbrec
;
73 struct acpi_fpdt_boot brec
;
75 r
= read_full_file("/sys/firmware/acpi/tables/FPDT", &buf
, &l
);
79 if (l
< sizeof(struct acpi_table_header
) + sizeof(struct acpi_fpdt_header
))
82 tbl
= (struct acpi_table_header
*)buf
;
86 if (memcmp(tbl
->signature
, "FPDT", 4) != 0)
89 /* find Firmware Basic Boot Performance Pointer Record */
90 for (rec
= (struct acpi_fpdt_header
*)(buf
+ sizeof(struct acpi_table_header
));
91 (char *)rec
< buf
+ l
;
92 rec
= (struct acpi_fpdt_header
*)((char *)rec
+ rec
->length
)) {
95 if (rec
->type
!= ACPI_FPDT_TYPE_BOOT
)
97 if (rec
->length
!= sizeof(struct acpi_fpdt_header
))
107 /* read Firmware Basic Boot Performance Data Record */
108 fd
= open("/dev/mem", O_CLOEXEC
|O_RDONLY
);
112 l
= pread(fd
, &hbrec
, sizeof(struct acpi_fpdt_boot_header
), ptr
);
113 if (l
!= sizeof(struct acpi_fpdt_boot_header
))
116 if (memcmp(hbrec
.signature
, "FBPT", 4) != 0)
119 if (hbrec
.length
< sizeof(struct acpi_fpdt_boot_header
) + sizeof(struct acpi_fpdt_boot
))
122 l
= pread(fd
, &brec
, sizeof(struct acpi_fpdt_boot
), ptr
+ sizeof(struct acpi_fpdt_boot_header
));
123 if (l
!= sizeof(struct acpi_fpdt_boot
))
126 if (brec
.length
!= sizeof(struct acpi_fpdt_boot
))
129 if (brec
.type
!= ACPI_FPDT_BOOT_REC
)
132 if (brec
.exit_services_exit
== 0)
133 /* Non-UEFI compatible boot. */
136 if (brec
.startup_start
== 0 || brec
.exit_services_exit
< brec
.startup_start
)
138 if (brec
.exit_services_exit
> NSEC_PER_HOUR
)
142 *loader_start
= brec
.startup_start
/ 1000;
144 *loader_exit
= brec
.exit_services_exit
/ 1000;